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