diff --git a/.php-cs-fixer.dist.php b/.php-cs-fixer.dist.php index 30655446..ad4f3336 100644 --- a/.php-cs-fixer.dist.php +++ b/.php-cs-fixer.dist.php @@ -9,6 +9,7 @@ $finder = (new Finder())->in([ __DIR__ . '/config', __DIR__ . '/src', + __DIR__ . '/stubs', __DIR__ . '/tests', ]); diff --git a/psalm.xml b/psalm.xml index 849e4c85..c3c1b590 100644 --- a/psalm.xml +++ b/psalm.xml @@ -12,16 +12,8 @@ - - - - - - - - diff --git a/tests/App/MemoryAdapter.php b/stubs/InMemoryAdapter.php similarity index 67% rename from tests/App/MemoryAdapter.php rename to stubs/InMemoryAdapter.php index 53c1c5cc..917fa14b 100644 --- a/tests/App/MemoryAdapter.php +++ b/stubs/InMemoryAdapter.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Yiisoft\Queue\Tests\App; +namespace Yiisoft\Queue\Stubs; use Yiisoft\Queue\Adapter\AdapterInterface; use Yiisoft\Queue\Message\IdEnvelope; @@ -11,9 +11,21 @@ use function count; -final class MemoryAdapter implements AdapterInterface +/** + * In-memory implementation of {@see AdapterInterface} for testing and development purposes. + * + * This adapter stores messages in a local array and processes them sequentially within the same process without any + * external queue backend. Messages are assigned incremental integer identifiers and are handled in FIFO order. + * + * Note: This implementation is not persistent and should not be used in production, as all data is lost when the + * process ends. + */ +final class InMemoryAdapter implements AdapterInterface { - /** @var array */ + /** + * @var MessageInterface[] + * @psalm-var array + */ private array $messages = []; private int $current = 0; diff --git a/stubs/StubAdapter.php b/stubs/StubAdapter.php deleted file mode 100644 index 6acbbac5..00000000 --- a/stubs/StubAdapter.php +++ /dev/null @@ -1,33 +0,0 @@ - $adapter, + AdapterInterface::class => new InMemoryAdapter(), ]); $provider = new QueueFactoryProvider( diff --git a/tests/Unit/QueueTest.php b/tests/Unit/QueueTest.php index 2f9236f9..fa571f83 100644 --- a/tests/Unit/QueueTest.php +++ b/tests/Unit/QueueTest.php @@ -8,8 +8,8 @@ use Yiisoft\Queue\Message\IdEnvelope; use Yiisoft\Queue\Message\Message; use Yiisoft\Queue\MessageStatus; +use Yiisoft\Queue\Stubs\InMemoryAdapter; use Yiisoft\Queue\Tests\App\FakeAdapter; -use Yiisoft\Queue\Tests\App\MemoryAdapter; use Yiisoft\Queue\Tests\TestCase; use function extension_loaded; @@ -73,7 +73,7 @@ public function testStatusReturnsNotFoundWithoutAdapter(): void public function testRunWithAdapter(): void { - $queue = $this->createQueue(new MemoryAdapter()); + $queue = $this->createQueue(new InMemoryAdapter()); $message = new Message('simple', null); $queue->push($message); $queue->push(clone $message); @@ -84,7 +84,7 @@ public function testRunWithAdapter(): void public function testRunPartlyWithAdapter(): void { - $queue = $this->createQueue(new MemoryAdapter()); + $queue = $this->createQueue(new InMemoryAdapter()); $message = new Message('simple', null); $queue->push($message); $queue->push(clone $message); @@ -95,7 +95,7 @@ public function testRunPartlyWithAdapter(): void public function testListenWithAdapter(): void { - $queue = $this->createQueue(new MemoryAdapter()); + $queue = $this->createQueue(new InMemoryAdapter()); $message = new Message('simple', null); $queue->push($message); $queue->push(clone $message); @@ -107,7 +107,7 @@ public function testListenWithAdapter(): void public function testStatusWithAdapter(): void { - $queue = $this->createQueue(new MemoryAdapter()); + $queue = $this->createQueue(new InMemoryAdapter()); $envelope = $queue->push(new Message('simple', null)); self::assertArrayHasKey(IdEnvelope::MESSAGE_ID_KEY, $envelope->getMetadata()); diff --git a/tests/Unit/Stubs/InMemoryAdapterTest.php b/tests/Unit/Stubs/InMemoryAdapterTest.php new file mode 100644 index 00000000..a286e7db --- /dev/null +++ b/tests/Unit/Stubs/InMemoryAdapterTest.php @@ -0,0 +1,173 @@ +push(new Message('test', 'a')); + $envelope2 = $adapter->push(new Message('test', 'b')); + $envelope3 = $adapter->push(new Message('test', 'c')); + + $this->assertInstanceOf(IdEnvelope::class, $envelope1); + $this->assertInstanceOf(IdEnvelope::class, $envelope2); + $this->assertInstanceOf(IdEnvelope::class, $envelope3); + + $this->assertSame(0, $envelope1->getId()); + $this->assertSame('a', $envelope1->getMessage()->getData()); + $this->assertSame(1, $envelope2->getId()); + $this->assertSame('b', $envelope2->getMessage()->getData()); + $this->assertSame(2, $envelope3->getId()); + $this->assertSame('c', $envelope3->getMessage()->getData()); + } + + public function testStatusWaitingForPushedMessage(): void + { + $adapter = new InMemoryAdapter(); + $envelope = $adapter->push(new Message('test', null)); + + $this->assertInstanceOf(IdEnvelope::class, $envelope); + $this->assertSame(MessageStatus::WAITING, $adapter->status($envelope->getId())); + } + + public function testStatusDoneAfterProcessing(): void + { + $adapter = new InMemoryAdapter(); + $envelope = $adapter->push(new Message('test', null)); + + $adapter->runExisting(static fn() => true); + + $this->assertInstanceOf(IdEnvelope::class, $envelope); + $this->assertSame(MessageStatus::DONE, $adapter->status($envelope->getId())); + } + + public function testStatusNotFoundForNonExistentId(): void + { + $adapter = new InMemoryAdapter(); + + $this->assertSame(MessageStatus::NOT_FOUND, $adapter->status(99)); + } + + public function testStatusNotFoundForNegativeId(): void + { + $adapter = new InMemoryAdapter(); + + $this->assertSame(MessageStatus::NOT_FOUND, $adapter->status(-1)); + } + + public function testStatusAcceptsStringId(): void + { + $adapter = new InMemoryAdapter(); + $envelope = $adapter->push(new Message('test', null)); + + $this->assertInstanceOf(IdEnvelope::class, $envelope); + $this->assertSame(MessageStatus::WAITING, $adapter->status((string) $envelope->getId())); + } + + public function testRunExistingProcessesAllMessages(): void + { + $adapter = new InMemoryAdapter(); + $adapter->push(new Message('test', 'a')); + $adapter->push(new Message('test', 'b')); + $adapter->push(new Message('test', 'c')); + + $processed = []; + $adapter->runExisting( + static function (MessageInterface $message) use (&$processed): bool { + $processed[] = $message->getData(); + return true; + }, + ); + + $this->assertSame(['a', 'b', 'c'], $processed); + } + + public function testRunExistingStopsWhenHandlerReturnsFalse(): void + { + $adapter = new InMemoryAdapter(); + $adapter->push(new Message('test', 'a')); + $adapter->push(new Message('test', 'b')); + $adapter->push(new Message('test', 'c')); + + $processed = []; + $adapter->runExisting( + static function (MessageInterface $message) use (&$processed): bool { + $processed[] = $message->getData(); + return false; + }, + ); + + $this->assertSame(['a'], $processed); + } + + public function testRunExistingOnEmptyQueue(): void + { + $adapter = new InMemoryAdapter(); + + $called = false; + $adapter->runExisting(static function () use (&$called): bool { + $called = true; + return true; + }); + + $this->assertFalse($called); + } + + public function testRunExistingDoesNotReprocessMessages(): void + { + $adapter = new InMemoryAdapter(); + $adapter->push(new Message('test', 'x')); + + $count = 0; + $handler = static function () use (&$count): bool { + $count++; + return true; + }; + $adapter->runExisting($handler); + $adapter->runExisting($handler); + + $this->assertSame(1, $count); + } + + public function testIdContinuesAfterProcessing(): void + { + $adapter = new InMemoryAdapter(); + $adapter->push(new Message('test', null)); + $adapter->runExisting(static fn() => true); + + $envelope = $adapter->push(new Message('test', null)); + + $this->assertInstanceOf(IdEnvelope::class, $envelope); + $this->assertSame(1, $envelope->getId()); + $this->assertSame(MessageStatus::WAITING, $adapter->status($envelope->getId())); + } + + public function testSubscribeProcessesExistingMessages(): void + { + $adapter = new InMemoryAdapter(); + $adapter->push(new Message('test', 'a')); + $adapter->push(new Message('test', 'b')); + + $processed = []; + $adapter->subscribe( + static function (MessageInterface $message) use (&$processed): bool { + $processed[] = $message->getData(); + return true; + }, + ); + + $this->assertSame(['a', 'b'], $processed); + } +} diff --git a/tests/Unit/Stubs/StubAdapterTest.php b/tests/Unit/Stubs/StubAdapterTest.php deleted file mode 100644 index 8f455c6e..00000000 --- a/tests/Unit/Stubs/StubAdapterTest.php +++ /dev/null @@ -1,25 +0,0 @@ -assertSame($message, $adapter->push($message)); - $this->assertSame(MessageStatus::DONE, $adapter->status('test')); - $adapter->runExisting(static fn(MessageInterface $message): bool => true); - $adapter->subscribe(static fn(MessageInterface $message): bool => true); - } -}