diff --git a/src/Exception/InvalidStatusException.php b/src/Exception/InvalidStatusException.php deleted file mode 100644 index 42d0cf31..00000000 --- a/src/Exception/InvalidStatusException.php +++ /dev/null @@ -1,48 +0,0 @@ -status = $status; - } - - public function getName(): string - { - return 'Invalid job status provided'; - } - - public function getSolution(): ?string - { - return <<status; - } -} diff --git a/src/Message/EnvelopeTrait.php b/src/Message/EnvelopeTrait.php index e8d19d65..68ae0d88 100644 --- a/src/Message/EnvelopeTrait.php +++ b/src/Message/EnvelopeTrait.php @@ -15,7 +15,7 @@ abstract public static function fromMessage(MessageInterface $message): self; public static function fromData(string $handlerName, mixed $data, array $metadata = []): MessageInterface { - return self::fromMessage(Message::fromData($handlerName, $data, $metadata)); + return static::fromMessage(Message::fromData($handlerName, $data, $metadata)); } public function getMessage(): MessageInterface @@ -46,8 +46,8 @@ public function getMetadata(): array return array_merge( $this->message->getMetadata(), [ - self::ENVELOPE_STACK_KEY => array_merge( - $this->message->getMetadata()[self::ENVELOPE_STACK_KEY] ?? [], + EnvelopeInterface::ENVELOPE_STACK_KEY => array_merge( + $this->message->getMetadata()[EnvelopeInterface::ENVELOPE_STACK_KEY] ?? [], [self::class], ), ], diff --git a/tests/App/DummyEnvelope.php b/tests/App/DummyEnvelope.php new file mode 100644 index 00000000..65c6162f --- /dev/null +++ b/tests/App/DummyEnvelope.php @@ -0,0 +1,22 @@ +message = $message; + + return $instance; + } +} diff --git a/tests/App/StaticMessageHandler.php b/tests/App/StaticMessageHandler.php new file mode 100644 index 00000000..3825836a --- /dev/null +++ b/tests/App/StaticMessageHandler.php @@ -0,0 +1,15 @@ +assertFalse($instance->memoryLimitReached()); + } + + public function testMemoryLimitNotReachedWhenUsageIsLower(): void + { + $currentMemoryUsage = memory_get_usage(true); + $instance = new class ($currentMemoryUsage + 1024 * 1024) { // 1MB higher than current usage + use SoftLimitTrait { + memoryLimitReached as public; + } + + public function __construct(private int $limit) + { + } + + protected function getMemoryLimit(): int + { + return $this->limit; + } + }; + + $this->assertFalse($instance->memoryLimitReached()); + } + + public function testMemoryLimitReachedWhenUsageIsHigher(): void + { + $currentMemoryUsage = memory_get_usage(true); + $instance = new class ($currentMemoryUsage - 1024) { // 1KB lower than current usage + use SoftLimitTrait { + memoryLimitReached as public; + } + + public function __construct(private int $limit) + { + } + + protected function getMemoryLimit(): int + { + return $this->limit; + } + }; + + $this->assertTrue($instance->memoryLimitReached()); + } + + public function testMemoryLimitExceededWhenUsageIncreases(): void + { + $currentMemoryUsage = memory_get_usage(true); + $instance = new class ($currentMemoryUsage + 5 * 1024 * 1024) { // Set limit 5MB higher than current usage + use SoftLimitTrait { + memoryLimitReached as public; + } + + public function __construct(private int $limit) + { + } + + protected function getMemoryLimit(): int + { + return $this->limit; + } + }; + + // Initially memory limit is not reached + $this->assertFalse($instance->memoryLimitReached()); + + // Create a large string to increase memory usage + $largeString = str_repeat('x', 5 * 1024 * 1024 + 1); // 5MB and 1 byte string + + // Now memory limit should be exceeded + $this->assertTrue($instance->memoryLimitReached()); + + // Clean up to free memory + unset($largeString); + } +} diff --git a/tests/Unit/Message/EnvelopeTraitTest.php b/tests/Unit/Message/EnvelopeTraitTest.php new file mode 100644 index 00000000..5dd5c4a6 --- /dev/null +++ b/tests/Unit/Message/EnvelopeTraitTest.php @@ -0,0 +1,49 @@ + 'value']; + $metadata = ['meta' => 'data']; + + $envelope = DummyEnvelope::fromData($handlerName, $data, $metadata); + + $this->assertInstanceOf(DummyEnvelope::class, $envelope); + $this->assertSame($handlerName, $envelope->getHandlerName()); + $this->assertSame($data, $envelope->getData()); + $this->assertArrayHasKey('meta', $envelope->getMetadata()); + $this->assertSame('data', $envelope->getMetadata()['meta']); + } + + public function testWithMessage(): void + { + $originalMessage = new Message('original-handler', 'original-data'); + $newMessage = new Message('new-handler', 'new-data'); + + $envelope = $this->createTestEnvelope(); + $envelope = $envelope->withMessage($originalMessage); + + $this->assertSame($originalMessage, $envelope->getMessage()); + + $newEnvelope = $envelope->withMessage($newMessage); + + $this->assertNotSame($envelope, $newEnvelope); + $this->assertSame($newMessage, $newEnvelope->getMessage()); + $this->assertSame($originalMessage, $envelope->getMessage()); + } +} diff --git a/tests/Unit/Message/JsonMessageSerializerTest.php b/tests/Unit/Message/JsonMessageSerializerTest.php index 18bc5ed9..93889d3b 100644 --- a/tests/Unit/Message/JsonMessageSerializerTest.php +++ b/tests/Unit/Message/JsonMessageSerializerTest.php @@ -5,6 +5,7 @@ namespace Yiisoft\Queue\Tests\Unit\Message; use InvalidArgumentException; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; use Yiisoft\Queue\Message\EnvelopeInterface; use Yiisoft\Queue\Message\IdEnvelope; @@ -18,6 +19,55 @@ */ final class JsonMessageSerializerTest extends TestCase { + /** + * @dataProvider dataUnsupportedHandlerNameFormat + */ + #[DataProvider('dataUnsupportedHandlerNameFormat')] + public function testHandlerNameFormat(mixed $name): void + { + $payload = ['name' => $name, 'data' => 'test']; + $serializer = $this->createSerializer(); + + $this->expectExceptionMessage(sprintf('Handler name must be a string. Got %s.', get_debug_type($name))); + $this->expectException(InvalidArgumentException::class); + $serializer->unserialize(json_encode($payload)); + } + + public static function dataUnsupportedHandlerNameFormat(): iterable + { + yield 'number' => [1]; + yield 'boolean' => [true]; + yield 'null' => [null]; + yield 'array' => [[]]; + } + + public function testDefaultMessageClassFallbackWrongClass(): void + { + $serializer = $this->createSerializer(); + $payload = [ + 'name' => 'handler', + 'data' => 'test', + 'meta' => [ + 'message-class' => 'NonExistentClass', + ], + ]; + + $message = $serializer->unserialize(json_encode($payload)); + $this->assertInstanceOf(Message::class, $message); + } + + public function testDefaultMessageClassFallbackClassNotSet(): void + { + $serializer = $this->createSerializer(); + $payload = [ + 'name' => 'handler', + 'data' => 'test', + 'meta' => [], + ]; + $message = $serializer->unserialize(json_encode($payload)); + $this->assertInstanceOf(Message::class, $message); + } + /** * @dataProvider dataUnsupportedPayloadFormat */ diff --git a/tests/Unit/WorkerTest.php b/tests/Unit/WorkerTest.php index d0fc9733..74bbf539 100644 --- a/tests/Unit/WorkerTest.php +++ b/tests/Unit/WorkerTest.php @@ -18,6 +18,7 @@ use Yiisoft\Queue\Middleware\FailureHandling\MiddlewareFactoryFailureInterface; use Yiisoft\Queue\QueueInterface; use Yiisoft\Queue\Tests\App\FakeHandler; +use Yiisoft\Queue\Tests\App\StaticMessageHandler; use Yiisoft\Queue\Tests\TestCase; use Yiisoft\Queue\Worker\Worker; @@ -199,4 +200,57 @@ private function createWorkerByParams( new FailureMiddlewareDispatcher($this->createMock(MiddlewareFactoryFailureInterface::class), []), ); } + + public function testHandlerNotFoundInContainer(): void + { + $message = new Message('nonexistent', ['test-data']); + $logger = new SimpleLogger(); + $container = new SimpleContainer(); + $handlers = []; + + $queue = $this->createMock(QueueInterface::class); + $worker = $this->createWorkerByParams($handlers, $logger, $container); + + $this->expectException(\RuntimeException::class); + $this->expectExceptionMessage('Queue handler with name "nonexistent" does not exist'); + $worker->process($message, $queue); + } + + public function testHandlerInContainerNotImplementingInterface(): void + { + $message = new Message('invalid', ['test-data']); + $logger = new SimpleLogger(); + $container = new SimpleContainer([ + 'invalid' => new class () { + public function handle(): void + { + } + }, + ]); + $handlers = []; + + $queue = $this->createMock(QueueInterface::class); + $worker = $this->createWorkerByParams($handlers, $logger, $container); + + $this->expectException(\RuntimeException::class); + $this->expectExceptionMessage('Queue handler with name "invalid" does not exist'); + $worker->process($message, $queue); + } + + public function testStaticMethodHandler(): void + { + $message = new Message('static-handler', ['test-data']); + $logger = new SimpleLogger(); + $container = new SimpleContainer(); + $handlers = [ + 'static-handler' => StaticMessageHandler::handle(...), + ]; + + $queue = $this->createMock(QueueInterface::class); + $worker = $this->createWorkerByParams($handlers, $logger, $container); + + StaticMessageHandler::$wasHandled = false; + $worker->process($message, $queue); + $this->assertTrue(StaticMessageHandler::$wasHandled); + } }