From 87c18faadb1513ded1fe6978687bf1b93343af38 Mon Sep 17 00:00:00 2001 From: michalsn Date: Wed, 24 Jun 2026 20:02:50 +0200 Subject: [PATCH] fix: test isolation for queue backends --- src/Handlers/RabbitMQHandler.php | 30 ++- tests/PushAndPopWithDelayTest.php | 76 +++--- tests/RabbitMQDelayTest.php | 51 ++-- tests/RabbitMQHandlerTest.php | 217 ++++++++++-------- tests/_support/CLITestCase.php | 9 + .../Database/Seeds/TestRedisQueueSeeder.php | 5 +- 6 files changed, 223 insertions(+), 165 deletions(-) diff --git a/src/Handlers/RabbitMQHandler.php b/src/Handlers/RabbitMQHandler.php index d2f7692..e207043 100644 --- a/src/Handlers/RabbitMQHandler.php +++ b/src/Handlers/RabbitMQHandler.php @@ -35,8 +35,9 @@ class RabbitMQHandler extends BaseHandler { private readonly AbstractConnection $connection; private readonly AMQPChannel $channel; - private array $declaredQueues = []; - private array $declaredExchanges = []; + private array $declaredLogicalQueues = []; + private array $declaredQueues = []; + private array $declaredExchanges = []; public function __construct(protected QueueConfig $config) { @@ -273,8 +274,12 @@ public function clear(?string $queue = null): bool { try { if ($queue === null) { - // Clear all configured queues - foreach (array_keys($this->config->queuePriorities) as $queueName) { + $queueNames = array_unique([ + ...array_keys($this->config->queuePriorities), + ...array_keys($this->declaredLogicalQueues), + ]); + + foreach ($queueNames as $queueName) { $this->clearQueue($queueName); } } else { @@ -300,7 +305,8 @@ public function clear(?string $queue = null): bool */ private function declareQueue(string $queue): void { - $priorities = $this->config->queuePriorities[$queue] ?? ['default']; + $this->declaredLogicalQueues[$queue] = true; + $priorities = $this->config->queuePriorities[$queue] ?? ['default']; foreach ($priorities as $priority) { $queueName = $this->getQueueName($queue, $priority); @@ -459,16 +465,20 @@ private function publishWithOptionalConfirm(AMQPMessage $message, string $exchan */ private function clearQueue(string $queue): void { + // Purging a queue that does not exist closes the AMQP channel. + $this->declareQueue($queue); + $priorities = $this->config->queuePriorities[$queue] ?? ['default']; foreach ($priorities as $priority) { $queueName = $this->getQueueName($queue, $priority); - try { - $this->channel->queue_purge($queueName); - } catch (Throwable) { - // Queue might not exist, ignore - } + $this->channel->queue_purge($queueName); + } + + $delayQueueName = $this->getDelayQueueName($queue); + if (isset($this->declaredQueues[$delayQueueName])) { + $this->channel->queue_purge($delayQueueName); } } diff --git a/tests/PushAndPopWithDelayTest.php b/tests/PushAndPopWithDelayTest.php index f06b0f9..7ac34ae 100644 --- a/tests/PushAndPopWithDelayTest.php +++ b/tests/PushAndPopWithDelayTest.php @@ -44,43 +44,47 @@ public function testPushAndPopWithDelay(string $name, string $class): void Time::setTestNow('2023-12-29 14:15:16'); $handler = new $class($this->config); - $result = $handler->setDelay(MINUTE)->push('queue-delay', 'success', ['key1' => 'value1']); - - $this->assertNotNull($result); - - $result = $handler->push('queue-delay', 'success', ['key2' => 'value2']); - - $this->assertNotNull($result); - - if ($name === 'database') { - $this->seeInDatabase('queue_jobs', [ - 'queue' => 'queue-delay', - 'payload' => json_encode(['job' => 'success', 'data' => ['key1' => 'value1'], 'metadata' => []]), - 'available_at' => 1703859376, - ]); - - $this->seeInDatabase('queue_jobs', [ - 'queue' => 'queue-delay', - 'payload' => json_encode(['job' => 'success', 'data' => ['key2' => 'value2'], 'metadata' => []]), - 'available_at' => 1703859316, - ]); + $handler->clear('queue-delay'); + + try { + $result = $handler->setDelay(MINUTE)->push('queue-delay', 'success', ['key1' => 'value1']); + $this->assertTrue($result->getStatus()); + + $result = $handler->push('queue-delay', 'success', ['key2' => 'value2']); + $this->assertTrue($result->getStatus()); + + if ($name === 'database') { + $this->seeInDatabase('queue_jobs', [ + 'queue' => 'queue-delay', + 'payload' => json_encode(['job' => 'success', 'data' => ['key1' => 'value1'], 'metadata' => []]), + 'available_at' => 1703859376, + ]); + + $this->seeInDatabase('queue_jobs', [ + 'queue' => 'queue-delay', + 'payload' => json_encode(['job' => 'success', 'data' => ['key2' => 'value2'], 'metadata' => []]), + 'available_at' => 1703859316, + ]); + } + + $result = $handler->pop('queue-delay', ['default']); + $this->assertInstanceOf(QueueJob::class, $result); + $payload = ['job' => 'success', 'data' => ['key2' => 'value2'], 'metadata' => []]; + $this->assertSame($payload, $result->payload); + + $result = $handler->pop('queue-delay', ['default']); + $this->assertNull($result); + + // add 1 minute + Time::setTestNow('2023-12-29 14:16:16'); + + $result = $handler->pop('queue-delay', ['default']); + $this->assertInstanceOf(QueueJob::class, $result); + $payload = ['job' => 'success', 'data' => ['key1' => 'value1'], 'metadata' => []]; + $this->assertSame($payload, $result->payload); + } finally { + $handler->clear('queue-delay'); } - - $result = $handler->pop('queue-delay', ['default']); - $this->assertInstanceOf(QueueJob::class, $result); - $payload = ['job' => 'success', 'data' => ['key2' => 'value2'], 'metadata' => []]; - $this->assertSame($payload, $result->payload); - - $result = $handler->pop('queue-delay', ['default']); - $this->assertNull($result); - - // add 1 minute - Time::setTestNow('2023-12-29 14:16:16'); - - $result = $handler->pop('queue-delay', ['default']); - $this->assertInstanceOf(QueueJob::class, $result); - $payload = ['job' => 'success', 'data' => ['key1' => 'value1'], 'metadata' => []]; - $this->assertSame($payload, $result->payload); } public static function providePushAndPopWithDelay(): iterable diff --git a/tests/RabbitMQDelayTest.php b/tests/RabbitMQDelayTest.php index b4787f2..548fcf4 100644 --- a/tests/RabbitMQDelayTest.php +++ b/tests/RabbitMQDelayTest.php @@ -17,6 +17,7 @@ use CodeIgniter\Queue\Entities\QueueJob; use CodeIgniter\Queue\Handlers\RabbitMQHandler; use CodeIgniter\Queue\QueuePushResult; +use CodeIgniter\Test\ReflectionHelper; use PhpAmqpLib\Connection\AMQPConnectionFactory; use Tests\Support\Config\Queue as QueueConfig; use Tests\Support\TestCase; @@ -29,13 +30,17 @@ */ final class RabbitMQDelayTest extends TestCase { + use ReflectionHelper; + private ?RabbitMQHandler $handler = null; + private string $queue; protected function setUp(): void { parent::setUp(); - $config = config(QueueConfig::class); + $config = config(QueueConfig::class); + $this->queue = 'delay-test-queue-' . bin2hex(random_bytes(6)); // Skip tests if RabbitMQ is not available if (! $this->isRabbitMQAvailable()) { @@ -52,9 +57,8 @@ protected function setUp(): void protected function tearDown(): void { if ($this->handler !== null) { - // Clear test queues try { - $this->handler->clear('delay-test-queue'); + $this->deleteDeclaredResources(); } catch (Throwable) { // Ignore cleanup errors } @@ -70,23 +74,23 @@ public function testDelayedMessageWithRealTiming(): void $startTime = time(); // Push a delayed job - $result = $this->handler->setDelay($delaySeconds)->push('delay-test-queue', 'success', ['type' => 'delayed']); + $result = $this->handler->setDelay($delaySeconds)->push($this->queue, 'success', ['type' => 'delayed']); $this->assertInstanceOf(QueuePushResult::class, $result); $this->assertTrue($result->getStatus()); // Push an immediate job - $result = $this->handler->push('delay-test-queue', 'success', ['type' => 'immediate']); + $result = $this->handler->push($this->queue, 'success', ['type' => 'immediate']); $this->assertInstanceOf(QueuePushResult::class, $result); $this->assertTrue($result->getStatus()); // Should get immediate job first - $job = $this->handler->pop('delay-test-queue', ['default']); + $job = $this->handler->pop($this->queue, ['default']); $this->assertInstanceOf(QueueJob::class, $job); $this->assertSame('immediate', $job->payload['data']['type']); $this->handler->done($job); // Should not get delayed job yet (within first second) - $job = $this->handler->pop('delay-test-queue', ['default']); + $job = $this->handler->pop($this->queue, ['default']); $this->assertNull($job); // Wait for delay to expire (with a small buffer) @@ -94,7 +98,7 @@ public function testDelayedMessageWithRealTiming(): void sleep($waitTime); // Should now get the delayed job - $job = $this->handler->pop('delay-test-queue', ['default']); + $job = $this->handler->pop($this->queue, ['default']); $this->assertInstanceOf(QueueJob::class, $job); $this->assertSame('delayed', $job->payload['data']['type']); @@ -109,34 +113,34 @@ public function testDelayedMessageWithRealTiming(): void public function testMultipleDelayedJobsWithDifferentDelays(): void { // Push jobs with different delays - $result1 = $this->handler->setDelay(1)->push('delay-test-queue', 'success', ['order' => 'first', 'delay' => 1]); - $result2 = $this->handler->setDelay(3)->push('delay-test-queue', 'success', ['order' => 'second', 'delay' => 3]); - $result3 = $this->handler->push('delay-test-queue', 'success', ['order' => 'immediate', 'delay' => 0]); + $result1 = $this->handler->setDelay(1)->push($this->queue, 'success', ['order' => 'first', 'delay' => 1]); + $result2 = $this->handler->setDelay(3)->push($this->queue, 'success', ['order' => 'second', 'delay' => 3]); + $result3 = $this->handler->push($this->queue, 'success', ['order' => 'immediate', 'delay' => 0]); $this->assertTrue($result1->getStatus()); $this->assertTrue($result2->getStatus()); $this->assertTrue($result3->getStatus()); // Should get immediate job first - $job = $this->handler->pop('delay-test-queue', ['default']); + $job = $this->handler->pop($this->queue, ['default']); $this->assertInstanceOf(QueueJob::class, $job); $this->assertSame('immediate', $job->payload['data']['order']); $this->handler->done($job); // Wait 2 seconds - should get first delayed job sleep(2); - $job = $this->handler->pop('delay-test-queue', ['default']); + $job = $this->handler->pop($this->queue, ['default']); $this->assertInstanceOf(QueueJob::class, $job); $this->assertSame('first', $job->payload['data']['order']); $this->handler->done($job); // Should not get second job yet - $job = $this->handler->pop('delay-test-queue', ['default']); + $job = $this->handler->pop($this->queue, ['default']); $this->assertNull($job); // Wait another 2 seconds - should get second delayed job sleep(2); - $job = $this->handler->pop('delay-test-queue', ['default']); + $job = $this->handler->pop($this->queue, ['default']); $this->assertInstanceOf(QueueJob::class, $job); $this->assertSame('second', $job->payload['data']['order']); $this->handler->done($job); @@ -145,11 +149,11 @@ public function testMultipleDelayedJobsWithDifferentDelays(): void public function testZeroDelayWorksImmediately(): void { // Jobs with 0 delay should work immediately - $result = $this->handler->setDelay(0)->push('delay-test-queue', 'success', ['type' => 'zero-delay']); + $result = $this->handler->setDelay(0)->push($this->queue, 'success', ['type' => 'zero-delay']); $this->assertTrue($result->getStatus()); // Should be able to pop immediately - $job = $this->handler->pop('delay-test-queue', ['default']); + $job = $this->handler->pop($this->queue, ['default']); $this->assertInstanceOf(QueueJob::class, $job); $this->assertSame('zero-delay', $job->payload['data']['type']); @@ -163,4 +167,17 @@ private function isRabbitMQAvailable(): bool { return class_exists(AMQPConnectionFactory::class); } + + private function deleteDeclaredResources(): void + { + $channel = self::getPrivateProperty($this->handler, 'channel'); + + foreach (array_keys(self::getPrivateProperty($this->handler, 'declaredQueues')) as $queue) { + $channel->queue_delete($queue); + } + + foreach (array_keys(self::getPrivateProperty($this->handler, 'declaredExchanges')) as $exchange) { + $channel->exchange_delete($exchange); + } + } } diff --git a/tests/RabbitMQHandlerTest.php b/tests/RabbitMQHandlerTest.php index d9b26c2..01b7825 100644 --- a/tests/RabbitMQHandlerTest.php +++ b/tests/RabbitMQHandlerTest.php @@ -32,14 +32,27 @@ final class RabbitMQHandlerTest extends TestCase { use ReflectionHelper; + private string $customPriorityQueue; + private string $emptyQueue; private QueueConfig $config; private ?RabbitMQHandler $handler = null; + private string $priorityQueue; + private string $testQueue; + private string $testQueue1; + private string $testQueue2; protected function setUp(): void { parent::setUp(); - $this->config = config(QueueConfig::class); + $suffix = bin2hex(random_bytes(6)); + $this->testQueue = 'test-queue-' . $suffix; + $this->testQueue1 = 'test-queue-1-' . $suffix; + $this->testQueue2 = 'test-queue-2-' . $suffix; + $this->priorityQueue = 'priority-test-' . $suffix; + $this->customPriorityQueue = 'custom-priority-queue-' . $suffix; + $this->emptyQueue = 'empty-queue-' . $suffix; + $this->config = config(QueueConfig::class); // Skip tests if RabbitMQ is not available if (! $this->isRabbitMQAvailable()) { @@ -56,13 +69,8 @@ protected function setUp(): void protected function tearDown(): void { if ($this->handler !== null) { - // Clear test queues try { - $this->handler->clear('test-queue'); - $this->handler->clear('test-queue-1'); - $this->handler->clear('test-queue-2'); - $this->handler->clear('priority-test'); - $this->handler->clear('custom-priority-queue'); + $this->deleteDeclaredResources(); } catch (Throwable) { // Ignore cleanup errors } @@ -91,7 +99,7 @@ public function testRabbitMQConnectionFailure(): void public function testPushJob(): void { - $result = $this->handler->push('test-queue', 'success', ['message' => 'Hello World']); + $result = $this->handler->push($this->testQueue, 'success', ['message' => 'Hello World']); $this->assertInstanceOf(QueuePushResult::class, $result); $this->assertTrue($result->getStatus()); @@ -101,7 +109,7 @@ public function testPushJob(): void public function testPushJobWithDelay(): void { - $result = $this->handler->setDelay(30)->push('test-queue', 'success', ['message' => 'Delayed']); + $result = $this->handler->setDelay(30)->push($this->testQueue, 'success', ['message' => 'Delayed']); $this->assertInstanceOf(QueuePushResult::class, $result); $this->assertTrue($result->getStatus()); @@ -109,95 +117,79 @@ public function testPushJobWithDelay(): void public function testPushJobWithPriority(): void { - $this->config->queuePriorities['priority-test'] = ['high', 'default', 'low']; + $this->config->queuePriorities[$this->priorityQueue] = ['high', 'default', 'low']; - $result = $this->handler->setPriority('high')->push('priority-test', 'success', ['priority' => 'high']); + $result = $this->handler->setPriority('high')->push($this->priorityQueue, 'success', ['priority' => 'high']); $this->assertTrue($result->getStatus()); } public function testPopJob(): void { - $this->handler->push('test-queue', 'success', ['message' => 'Test Pop']); + $this->handler->push($this->testQueue, 'success', ['message' => 'Test Pop']); - // Give RabbitMQ a moment to process - usleep(100_000); + $job = $this->popEventually($this->testQueue, ['default']); - $job = $this->handler->pop('test-queue', ['default']); + $this->assertInstanceOf(QueueJob::class, $job); + $this->assertSame($this->testQueue, $job->queue); + $this->assertSame('success', $job->payload['job']); + $this->assertSame(['message' => 'Test Pop'], $job->payload['data']); - if ($job !== null) { - $this->assertInstanceOf(QueueJob::class, $job); - $this->assertSame('test-queue', $job->queue); - $this->assertSame('success', $job->payload['job']); - $this->assertSame(['message' => 'Test Pop'], $job->payload['data']); - - // Clean up - mark as done - $this->handler->done($job); - } + // Clean up - mark as done + $this->handler->done($job); } public function testPopJobWithPriorities(): void { - $this->config->queuePriorities['priority-test'] = ['high', 'default', 'low']; + $this->config->queuePriorities[$this->priorityQueue] = ['high', 'default', 'low']; // Push jobs with different priorities - $this->handler->setPriority('low')->push('priority-test', 'success', ['priority' => 'low']); - $this->handler->setPriority('high')->push('priority-test', 'success', ['priority' => 'high']); - $this->handler->setPriority('default')->push('priority-test', 'success', ['priority' => 'default']); - - usleep(100_000); + $this->handler->setPriority('low')->push($this->priorityQueue, 'success', ['priority' => 'low']); + $this->handler->setPriority('high')->push($this->priorityQueue, 'success', ['priority' => 'high']); + $this->handler->setPriority('default')->push($this->priorityQueue, 'success', ['priority' => 'default']); // Should get high priority job first - $job = $this->handler->pop('priority-test', ['high', 'default', 'low']); + $job = $this->popEventually($this->priorityQueue, ['high', 'default', 'low']); - if ($job !== null) { - $this->assertSame('high', $job->priority); - $this->handler->done($job); - } + $this->assertInstanceOf(QueueJob::class, $job); + $this->assertSame('high', $job->priority); + $this->handler->done($job); } public function testJobFailure(): void { - $this->handler->push('test-queue', 'failure', ['message' => 'Will Fail']); - - usleep(100_000); + $this->handler->push($this->testQueue, 'failure', ['message' => 'Will Fail']); - $job = $this->handler->pop('test-queue', ['default']); + $job = $this->popEventually($this->testQueue, ['default']); - if ($job !== null) { - $exception = new Exception('Test failure'); - $result = $this->handler->failed($job, $exception, false); + $this->assertInstanceOf(QueueJob::class, $job); + $exception = new Exception('Test failure'); + $result = $this->handler->failed($job, $exception, false); - $this->assertTrue($result); - } + $this->assertTrue($result); } public function testJobLater(): void { - $this->handler->push('test-queue', 'success', ['message' => 'Reschedule']); + $this->handler->push($this->testQueue, 'success', ['message' => 'Reschedule']); - usleep(100_000); + $job = $this->popEventually($this->testQueue, ['default']); - $job = $this->handler->pop('test-queue', ['default']); - - if ($job !== null) { - $result = $this->handler->later($job, 60); - $this->assertTrue($result); - } + $this->assertInstanceOf(QueueJob::class, $job); + $result = $this->handler->later($job, 60); + $this->assertTrue($result); } public function testClearQueue(): void { - $this->handler->push('test-queue', 'success', ['message' => 'Clear Test 1']); - $this->handler->push('test-queue', 'success', ['message' => 'Clear Test 2']); - - usleep(100_000); + $this->handler->push($this->testQueue, 'success', ['message' => 'Clear Test 1']); + $this->handler->push($this->testQueue, 'success', ['message' => 'Clear Test 2']); - $result = $this->handler->clear('test-queue'); + $result = $this->handler->clear($this->testQueue); $this->assertTrue($result); // Verify queue is empty - $job = $this->handler->pop('test-queue', ['default']); + $job = $this->handler->pop($this->testQueue, ['default']); $this->assertNull($job); } @@ -205,7 +197,7 @@ public function testIncorrectJobHandler(): void { $this->expectException(QueueException::class); - $this->handler->push('test-queue', 'nonexistent-job', []); + $this->handler->push($this->testQueue, 'nonexistent-job', []); } public function testIncorrectQueueFormat(): void @@ -219,47 +211,42 @@ public function testIncorrectPriority(): void { $this->expectException(QueueException::class); - $this->config->queuePriorities['test-queue'] = ['high', 'low']; + $this->config->queuePriorities[$this->testQueue] = ['high', 'low']; - $this->handler->setPriority('medium')->push('test-queue', 'success', []); + $this->handler->setPriority('medium')->push($this->testQueue, 'success', []); } public function testCustomPriorityMapping(): void { // Define custom priorities for a queue - $this->config->queuePriorities['custom-priority-queue'] = ['urgent', 'normal', 'low']; + $this->config->queuePriorities[$this->customPriorityQueue] = ['urgent', 'normal', 'low']; // Test that we can push jobs with custom priorities - $result1 = $this->handler->setPriority('urgent')->push('custom-priority-queue', 'success', ['priority' => 'urgent']); - $result2 = $this->handler->setPriority('normal')->push('custom-priority-queue', 'success', ['priority' => 'normal']); - $result3 = $this->handler->setPriority('low')->push('custom-priority-queue', 'success', ['priority' => 'low']); + $result1 = $this->handler->setPriority('urgent')->push($this->customPriorityQueue, 'success', ['priority' => 'urgent']); + $result2 = $this->handler->setPriority('normal')->push($this->customPriorityQueue, 'success', ['priority' => 'normal']); + $result3 = $this->handler->setPriority('low')->push($this->customPriorityQueue, 'success', ['priority' => 'low']); $this->assertTrue($result1->getStatus()); $this->assertTrue($result2->getStatus()); $this->assertTrue($result3->getStatus()); - usleep(100_000); - // Should get urgent priority job first - $job = $this->handler->pop('custom-priority-queue', ['urgent', 'normal', 'low']); - if ($job !== null) { - $this->assertSame('urgent', $job->payload['data']['priority']); - $this->handler->done($job); - } + $job = $this->popEventually($this->customPriorityQueue, ['urgent', 'normal', 'low']); + $this->assertInstanceOf(QueueJob::class, $job); + $this->assertSame('urgent', $job->payload['data']['priority']); + $this->handler->done($job); // Then normal priority - $job = $this->handler->pop('custom-priority-queue', ['urgent', 'normal', 'low']); - if ($job !== null) { - $this->assertSame('normal', $job->payload['data']['priority']); - $this->handler->done($job); - } + $job = $this->handler->pop($this->customPriorityQueue, ['urgent', 'normal', 'low']); + $this->assertInstanceOf(QueueJob::class, $job); + $this->assertSame('normal', $job->payload['data']['priority']); + $this->handler->done($job); // Finally low priority - $job = $this->handler->pop('custom-priority-queue', ['urgent', 'normal', 'low']); - if ($job !== null) { - $this->assertSame('low', $job->payload['data']['priority']); - $this->handler->done($job); - } + $job = $this->handler->pop($this->customPriorityQueue, ['urgent', 'normal', 'low']); + $this->assertInstanceOf(QueueJob::class, $job); + $this->assertSame('low', $job->payload['data']['priority']); + $this->handler->done($job); } public function testPriority(): void @@ -279,15 +266,15 @@ public function testPriorityException(): void public function testPopEmpty(): void { - $result = $this->handler->pop('empty-queue', ['default']); + $result = $this->handler->pop($this->emptyQueue, ['default']); $this->assertNull($result); } public function testFailedAndKeepJob(): void { - $this->handler->push('test-queue', 'success', ['test' => 'data']); - $queueJob = $this->handler->pop('test-queue', ['default']); + $this->handler->push($this->testQueue, 'success', ['test' => 'data']); + $queueJob = $this->handler->pop($this->testQueue, ['default']); $this->assertInstanceOf(QueueJob::class, $queueJob); @@ -297,15 +284,15 @@ public function testFailedAndKeepJob(): void $this->assertTrue($result); $this->seeInDatabase('queue_jobs_failed', [ - 'queue' => 'test-queue', + 'queue' => $this->testQueue, 'connection' => 'rabbitmq', ]); } public function testFailedAndDontKeepJob(): void { - $this->handler->push('test-queue', 'success', ['test' => 'data']); - $queueJob = $this->handler->pop('test-queue', ['default']); + $this->handler->push($this->testQueue, 'success', ['test' => 'data']); + $queueJob = $this->handler->pop($this->testQueue, ['default']); $this->assertInstanceOf(QueueJob::class, $queueJob); @@ -315,15 +302,15 @@ public function testFailedAndDontKeepJob(): void $this->assertTrue($result); $this->dontSeeInDatabase('queue_jobs_failed', [ - 'queue' => 'test-queue', + 'queue' => $this->testQueue, 'connection' => 'rabbitmq', ]); } public function testDone(): void { - $this->handler->push('test-queue', 'success', ['test' => 'data']); - $queueJob = $this->handler->pop('test-queue', ['default']); + $this->handler->push($this->testQueue, 'success', ['test' => 'data']); + $queueJob = $this->handler->pop($this->testQueue, ['default']); $this->assertInstanceOf(QueueJob::class, $queueJob); @@ -335,13 +322,11 @@ public function testDone(): void public function testClearAll(): void { - $this->handler->push('test-queue-1', 'success', ['test' => 'data1']); - $this->handler->push('test-queue-2', 'success', ['test' => 'data2']); + $this->handler->push($this->testQueue1, 'success', ['test' => 'data1']); + $this->handler->push($this->testQueue2, 'success', ['test' => 'data2']); - usleep(100_000); - - $job1 = $this->handler->pop('test-queue-1', ['default']); - $job2 = $this->handler->pop('test-queue-2', ['default']); + $job1 = $this->popEventually($this->testQueue1, ['default']); + $job2 = $this->popEventually($this->testQueue2, ['default']); $this->assertInstanceOf(QueueJob::class, $job1); $this->assertInstanceOf(QueueJob::class, $job2); @@ -356,15 +341,13 @@ public function testClearAll(): void $channel->basic_nack($job2->amqpDeliveryTag, false, true); } - usleep(100_000); - // Clear all queues $result = $this->handler->clear(); $this->assertTrue($result); // Verify queues are empty by attempting to pop - $jobAfter1 = $this->handler->pop('test-queue-1', ['default']); - $jobAfter2 = $this->handler->pop('test-queue-2', ['default']); + $jobAfter1 = $this->handler->pop($this->testQueue1, ['default']); + $jobAfter2 = $this->handler->pop($this->testQueue2, ['default']); $this->assertNull($jobAfter1); $this->assertNull($jobAfter2); @@ -385,4 +368,36 @@ private function isRabbitMQAvailable(): bool { return class_exists(AMQPConnectionFactory::class); } + + private function deleteDeclaredResources(): void + { + $channel = self::getPrivateProperty($this->handler, 'channel'); + + foreach (array_keys(self::getPrivateProperty($this->handler, 'declaredQueues')) as $queue) { + $channel->queue_delete($queue); + } + + foreach (array_keys(self::getPrivateProperty($this->handler, 'declaredExchanges')) as $exchange) { + $channel->exchange_delete($exchange); + } + } + + /** + * @param list $priorities + */ + private function popEventually(string $queue, array $priorities, float $timeout = 1.0): ?QueueJob + { + $deadline = microtime(true) + $timeout; + + do { + $job = $this->handler->pop($queue, $priorities); + if ($job !== null) { + return $job; + } + + usleep(10_000); + } while (microtime(true) < $deadline); + + return null; + } } diff --git a/tests/_support/CLITestCase.php b/tests/_support/CLITestCase.php index f0d405d..f134062 100644 --- a/tests/_support/CLITestCase.php +++ b/tests/_support/CLITestCase.php @@ -14,6 +14,7 @@ namespace Tests\Support; use CodeIgniter\CLI\CLI; +use CodeIgniter\Test\Filters\CITestStreamFilter; use CodeIgniter\Test\ReflectionHelper; abstract class CLITestCase extends TestCase @@ -22,6 +23,14 @@ abstract class CLITestCase extends TestCase private array $lines = []; + protected function tearDown(): void + { + CITestStreamFilter::removeErrorFilter(); + CITestStreamFilter::removeOutputFilter(); + + parent::tearDown(); + } + protected function parseOutput(string $output): string { $this->lines = []; diff --git a/tests/_support/Database/Seeds/TestRedisQueueSeeder.php b/tests/_support/Database/Seeds/TestRedisQueueSeeder.php index 4fe4233..d14d86f 100644 --- a/tests/_support/Database/Seeds/TestRedisQueueSeeder.php +++ b/tests/_support/Database/Seeds/TestRedisQueueSeeder.php @@ -40,7 +40,10 @@ public function run(): void throw new CriticalError('Queue: RedisException occurred with message (' . $e->getMessage() . ').'); } - $redis->flushDB(); + $keys = $redis->keys('queues:*'); + if ($keys !== []) { + $redis->del($keys); + } $jobQueue = new QueueJob([ 'id' => '1234567890123456',