From 711e4cd8a6d1302ec40a5877598e118f2c3d8298 Mon Sep 17 00:00:00 2001 From: bota Date: Mon, 14 Jul 2025 16:21:15 +0300 Subject: [PATCH 1/4] Issue #25: Tests to improve code coverage Signed-off-by: bota --- composer.json | 3 +- config/autoload/log.local.php.dist | 2 +- src/App/Message/ExampleMessageHandler.php | 11 +- test/App/AppConfigProviderTest.php | 23 ++ .../App/Message/ExampleMessageHandlerTest.php | 200 ++++++++++++++++++ test/App/Message/ExampleMessageTest.php | 17 ++ .../Factory/StartCommandFactoryTest.php | 27 +++ .../Factory/StopCommandFactoryTest.php | 35 +++ test/Swoole/Command/IsRunningTraitTest.php | 41 ++++ test/Swoole/Command/StartCommandTest.php | 92 ++++++++ test/Swoole/Command/StopCommandTest.php | 90 ++++++++ test/Swoole/Delegators/DummySwooleServer.php | 38 ++++ .../Delegators/TCPServerDelegatorTest.php | 101 +++++++++ ...dStaticResourceMiddlewareExceptionTest.php | 32 +++ test/Swoole/PidManagerFactoryTest.php | 54 +++++ test/Swoole/PidManagerTest.php | 92 ++++++++ test/Swoole/ServerFactoryTest.php | 153 ++++++++++++++ .../{ => Swoole}/SwooleConfigProviderTest.php | 2 +- 18 files changed, 1005 insertions(+), 8 deletions(-) create mode 100644 test/App/AppConfigProviderTest.php create mode 100644 test/App/Message/ExampleMessageHandlerTest.php create mode 100644 test/App/Message/ExampleMessageTest.php create mode 100644 test/Swoole/Command/Factory/StartCommandFactoryTest.php create mode 100644 test/Swoole/Command/Factory/StopCommandFactoryTest.php create mode 100644 test/Swoole/Command/IsRunningTraitTest.php create mode 100644 test/Swoole/Command/StartCommandTest.php create mode 100644 test/Swoole/Command/StopCommandTest.php create mode 100644 test/Swoole/Delegators/DummySwooleServer.php create mode 100644 test/Swoole/Delegators/TCPServerDelegatorTest.php create mode 100644 test/Swoole/Exception/InvalidStaticResourceMiddlewareExceptionTest.php create mode 100644 test/Swoole/PidManagerFactoryTest.php create mode 100644 test/Swoole/PidManagerTest.php create mode 100644 test/Swoole/ServerFactoryTest.php rename test/{ => Swoole}/SwooleConfigProviderTest.php (93%) diff --git a/composer.json b/composer.json index f532977..bb71273 100644 --- a/composer.json +++ b/composer.json @@ -84,7 +84,8 @@ }, "autoload-dev": { "psr-4": { - "DotTest\\Mail\\": "test/" + "QueueTest\\App\\": "test/App/", + "QueueTest\\Swoole\\": "test/Swoole/" } }, "scripts": { diff --git a/config/autoload/log.local.php.dist b/config/autoload/log.local.php.dist index 1422cc8..913af79 100644 --- a/config/autoload/log.local.php.dist +++ b/config/autoload/log.local.php.dist @@ -10,7 +10,7 @@ return [ 'writers' => [ 'FileWriter' => [ 'name' => 'stream', - 'level' => \Dot\Log\Logger::ALERT, // this is equal to 1 + 'priority' => \Dot\Log\Logger::ALERT, // this is equal to 1 'options' => [ 'stream' => __DIR__ . '/../../log/queue-log.log', 'formatter' => [ diff --git a/src/App/Message/ExampleMessageHandler.php b/src/App/Message/ExampleMessageHandler.php index 237a641..9f5a4b6 100644 --- a/src/App/Message/ExampleMessageHandler.php +++ b/src/App/Message/ExampleMessageHandler.php @@ -42,11 +42,12 @@ public function __invoke(ExampleMessage $message): void if ($payload !== null && isset($payload['userUuid'])) { $this->logger->info("message: " . $payload['userUuid']); $this->args = $payload; - } - try { - $this->perform(); - } catch (Exception $exception) { + try { + $this->perform(); + } catch (Exception $exception) { + $this->logger->err("message: " . $exception->getMessage()); + } } } @@ -64,7 +65,7 @@ public function perform(): void public function sendWelcomeMail(): bool { $user = $this->userRepository->find($this->args['userUuid']); - $this->mailService->getMessage()->addTo('sergiubota@rospace.com', 'sergiu'); + $this->mailService->getMessage()->addTo($user->getEmail(), $user->getName()); $this->mailService->setSubject('Welcome to ' . $this->config['application']['name']); $body = $this->templateRenderer->render('notification-email::welcome', [ 'user' => $user, diff --git a/test/App/AppConfigProviderTest.php b/test/App/AppConfigProviderTest.php new file mode 100644 index 0000000..62590b6 --- /dev/null +++ b/test/App/AppConfigProviderTest.php @@ -0,0 +1,23 @@ +config = (new ConfigProvider())(); + } + + public function testHasDependencies(): void + { + $this->assertArrayHasKey('dependencies', $this->config); + } +} diff --git a/test/App/Message/ExampleMessageHandlerTest.php b/test/App/Message/ExampleMessageHandlerTest.php new file mode 100644 index 0000000..ba2d274 --- /dev/null +++ b/test/App/Message/ExampleMessageHandlerTest.php @@ -0,0 +1,200 @@ +mailService = $this->createMock(MailService::class); + $this->renderer = $this->createMock(TemplateRendererInterface::class); + $this->userRepository = $this->createMock(UserRepository::class); + $this->logger = new Logger([ + 'writers' => [ + 'FileWriter' => [ + 'name' => 'null', + 'priority' => Logger::ALERT, + ], + ], + ]); + $this->config = [ + 'notification' => [ + 'server' => [ + 'protocol' => 'tcp', + 'host' => 'localhost', + 'port' => '8556', + 'eof' => "\n", + ], + ], + 'application' => [ + 'name' => 'dotkernel', + ], + ]; + + $this->handler = new ExampleMessageHandler( + $this->mailService, + $this->renderer, + $this->userRepository, + $this->logger, + $this->config + ); + } + + /** + * @throws \PHPUnit\Framework\MockObject\Exception + */ + public function testInvokeHandlesExceptionThrownByPerform(): void + { + $uuid = '1234'; + + $message = $this->createMock(ExampleMessage::class); + $message->method('getPayload')->willReturn([ + 'foo' => json_encode(['userUuid' => $uuid]), + ]); + + $handlerMock = $this->getMockBuilder(ExampleMessageHandler::class) + ->setConstructorArgs([ + $this->mailService, + $this->renderer, + $this->userRepository, + $this->logger, + $this->config, + ]) + ->onlyMethods(['perform']) + ->getMock(); + + $handlerMock + ->expects($this->once()) + ->method('perform') + ->willThrowException(new \RuntimeException('Test exception')); + + $handlerMock->__invoke($message); + } + + /** + * @throws \PHPUnit\Framework\MockObject\Exception + */ + public function testInvokeWithValidPayload(): void + { + $uuid = '1245'; + $email = 'test@dotkernel.com'; + $name = 'dotkernel'; + + $message = $this->createMock(ExampleMessage::class); + $message->method('getPayload')->willReturn([ + 'foo' => json_encode(['userUuid' => $uuid]), + ]); + + $user = $this->getMockBuilder(\stdClass::class) + ->addMethods(['getEmail', 'getName']) + ->getMock(); + $user->method('getEmail')->willReturn($email); + $user->method('getName')->willReturn($name); + + $this->userRepository + ->expects($this->once()) + ->method('find') + ->with($uuid) + ->willReturn($user); + + $mailMessage = $this->createMock(Email::class); + $mailMessage->expects($this->once()) + ->method('addTo') + ->with($email, $name); + + $this + ->mailService + ->method('getMessage') + ->willReturn($mailMessage); + + $this + ->mailService + ->expects($this->once()) + ->method('setSubject') + ->with('Welcome to dotkernel'); + + $this->renderer->method('render')->willReturn('Rendered email body'); + + $this->mailService->expects($this->once()) + ->method('setBody') + ->with('Rendered email body'); + + $this->handler->__invoke($message); + } + + /** + * @throws MailException + * @throws \PHPUnit\Framework\MockObject\Exception + */ + public function testSendWelcomeMailHandlesMailException(): void + { + $uuid = '1234'; + + $reflection = new \ReflectionClass($this->handler); + $argsProperty = $reflection->getProperty('args'); + $argsProperty->setValue($this->handler, ['userUuid' => $uuid]); + + $user = $this->getMockBuilder(\stdClass::class) + ->addMethods(['getEmail', 'getName']) + ->getMock(); + $user->method('getEmail')->willReturn('test@dotkernel.com'); + $user->method('getName')->willReturn('dotkernel'); + + $this->userRepository->method('find')->willReturn($user); + $this->mailService->method('getMessage')->willReturn($this->createMock(Email::class)); + $this->mailService->method('setSubject'); + $this->renderer->method('render')->willReturn('Rendered content'); + $this->mailService->method('setBody'); + + $this->mailService->method('send') + ->willThrowException($this->createMock(TransportExceptionInterface::class)); + + $result = $this->handler->sendWelcomeMail(); + $this->assertFalse($result); + } + + /** + * @throws \PHPUnit\Framework\MockObject\Exception + */ + public function testInvokeWithInvalidJsonSkipsPerform(): void + { + $message = $this->createMock(ExampleMessage::class); + $message->method('getPayload')->willReturn([ + 'foo' => '{"userUuid":', + ]); + + $this->userRepository->expects($this->never())->method('find'); + $this->mailService->expects($this->never())->method('send'); + + $this->handler->__invoke($message); + } +} diff --git a/test/App/Message/ExampleMessageTest.php b/test/App/Message/ExampleMessageTest.php new file mode 100644 index 0000000..7f00eaa --- /dev/null +++ b/test/App/Message/ExampleMessageTest.php @@ -0,0 +1,17 @@ + "test message payload"]); + $this->assertSame(["payload" => "test message payload"], $admin->getPayload()); + } +} diff --git a/test/Swoole/Command/Factory/StartCommandFactoryTest.php b/test/Swoole/Command/Factory/StartCommandFactoryTest.php new file mode 100644 index 0000000..40e63a0 --- /dev/null +++ b/test/Swoole/Command/Factory/StartCommandFactoryTest.php @@ -0,0 +1,27 @@ +createMock(ContainerInterface::class); + + $factory = new StartCommandFactory(); + $command = $factory($container); + + $this->assertContainsOnlyInstancesOf(StartCommand::class, [$command]); + } +} diff --git a/test/Swoole/Command/Factory/StopCommandFactoryTest.php b/test/Swoole/Command/Factory/StopCommandFactoryTest.php new file mode 100644 index 0000000..a2ca055 --- /dev/null +++ b/test/Swoole/Command/Factory/StopCommandFactoryTest.php @@ -0,0 +1,35 @@ +createMock(PidManager::class); + + $container = $this->createMock(ContainerInterface::class); + $container->expects($this->once()) + ->method('get') + ->with(PidManager::class) + ->willReturn($pidManager); + + $factory = new StopCommandFactory(); + + $command = $factory($container); + + $this->assertContainsOnlyInstancesOf(StopCommand::class, [$command]); + } +} diff --git a/test/Swoole/Command/IsRunningTraitTest.php b/test/Swoole/Command/IsRunningTraitTest.php new file mode 100644 index 0000000..ef59e0d --- /dev/null +++ b/test/Swoole/Command/IsRunningTraitTest.php @@ -0,0 +1,41 @@ +traitUser = new class { + use IsRunningTrait; + + public PidManager $pidManager; + }; + + $this->traitUser->pidManager = $this->createMock(PidManager::class); + } + + public function testIsRunningReturnsFalseWhenNoPids(): void + { + $this->traitUser->pidManager->method('read')->willReturn([]); + $this->assertFalse($this->traitUser->isRunning()); + } + + public function testIsRunningReturnsFalseWhenPidsAreZero(): void + { + $this->traitUser->pidManager->method('read')->willReturn([0, 0]); + $this->assertFalse($this->traitUser->isRunning()); + } +} diff --git a/test/Swoole/Command/StartCommandTest.php b/test/Swoole/Command/StartCommandTest.php new file mode 100644 index 0000000..246976b --- /dev/null +++ b/test/Swoole/Command/StartCommandTest.php @@ -0,0 +1,92 @@ +createMock(InputInterface::class); + $output = $this->createMock(OutputInterface::class); + $pidManager = $this->createMock(PidManager::class); + $server = $this->createMock(Server::class); + + $pidManager->method('read')->willReturn([]); + + $server->master_pid = 1234; + $server->manager_pid = 4321; + + $server->expects($this->once())->method('on'); + $server->expects($this->once())->method('start'); + + $config = [ + 'dotkernel-queue-swoole' => [ + 'swoole-server' => [ + 'process-name' => 'test-process', + ], + ], + ]; + + $container = $this->createMock(ContainerInterface::class); + $container->method('get')->willReturnCallback(function (string $id) use ($pidManager, $server, $config) { + return match ($id) { + PidManager::class => $pidManager, + Server::class => $server, + 'config' => $config, + default => null, + }; + }); + + $command = new StartCommand($container); + $statusCode = $command->run($input, $output); + + $this->assertSame(0, $statusCode); + } + + /** + * @throws ExceptionInterface + * @throws Exception + */ + public function testExecuteWhenServerIsAlreadyRunning(): void + { + $container = $this->createMock(ContainerInterface::class); + $pidManager = $this->createMock(PidManager::class); + $container->method('get') + ->with(PidManager::class) + ->willReturn($pidManager); + + $input = $this->createMock(InputInterface::class); + $output = $this->createMock(OutputInterface::class); + + $output->expects($this->once()) + ->method('writeln') + ->with('Server is already running!'); + + $command = $this->getMockBuilder(StartCommand::class) + ->setConstructorArgs([$container]) + ->onlyMethods(['isRunning']) + ->getMock(); + + $command->method('isRunning')->willReturn(true); + + $exitCode = $command->run($input, $output); + + $this->assertSame(1, $exitCode); + } +} diff --git a/test/Swoole/Command/StopCommandTest.php b/test/Swoole/Command/StopCommandTest.php new file mode 100644 index 0000000..dbdcca2 --- /dev/null +++ b/test/Swoole/Command/StopCommandTest.php @@ -0,0 +1,90 @@ +createMock(PidManager::class); + + $command = $this->getMockBuilder(StopCommand::class) + ->setConstructorArgs([$pidManager]) + ->onlyMethods(['isRunning']) + ->getMock(); + + $command->method('isRunning')->willReturn(false); + + $tester = new CommandTester($command); + $exitCode = $tester->execute([]); + + $this->assertSame(0, $exitCode); + $this->assertStringContainsString('Server is not running', $tester->getDisplay()); + } + + /** + * @throws Exception + */ + public function testExecuteWhenServerStopsSuccessfully(): void + { + $pidManager = $this->createMock(PidManager::class); + $pidManager->method('read')->willReturn(['1234']); + $pidManager->expects($this->once())->method('delete'); + + $command = $this->getMockBuilder(StopCommand::class) + ->setConstructorArgs([$pidManager]) + ->onlyMethods(['isRunning']) + ->getMock(); + + $command->method('isRunning')->willReturn(true); + + $command->killProcess = function (int $pid, ?int $signal = null): bool { + return true; + }; + + $tester = new CommandTester($command); + $exitCode = $tester->execute([]); + + $this->assertSame(0, $exitCode); + $this->assertStringContainsString('Server stopped', $tester->getDisplay()); + } + + /** + * @throws Exception + */ + public function testExecuteWhenServerFailsToStop(): void + { + $pidManager = $this->createMock(PidManager::class); + $pidManager->method('read')->willReturn(['1234']); + $pidManager->expects($this->never())->method('delete'); + + $command = $this->getMockBuilder(StopCommand::class) + ->setConstructorArgs([$pidManager]) + ->onlyMethods(['isRunning']) + ->getMock(); + + $command->method('isRunning')->willReturn(true); + $command->waitThreshold = 1; + + $command->killProcess = function (int $pid, ?int $signal = null): bool { + return $signal === 0; + }; + + $tester = new CommandTester($command); + $exitCode = $tester->execute([]); + + $this->assertSame(1, $exitCode); + $this->assertStringContainsString('Error stopping server', $tester->getDisplay()); + } +} diff --git a/test/Swoole/Delegators/DummySwooleServer.php b/test/Swoole/Delegators/DummySwooleServer.php new file mode 100644 index 0000000..248e6af --- /dev/null +++ b/test/Swoole/Delegators/DummySwooleServer.php @@ -0,0 +1,38 @@ + */ + public array $callbacks = []; + + public function __construct() + { + parent::__construct('127.0.0.1', 0); + } + + /** + * @param string $eventName + * @param callable $callback + */ + public function on($eventName, $callback): bool + { + $this->callbacks[$eventName] = $callback; + return true; + } + + /** + * @param int|string $fd + * @param string $data + * @param int $serverSocket + */ + public function send($fd, $data, $serverSocket = -1): bool + { + return true; + } +} diff --git a/test/Swoole/Delegators/TCPServerDelegatorTest.php b/test/Swoole/Delegators/TCPServerDelegatorTest.php new file mode 100644 index 0000000..78fb8df --- /dev/null +++ b/test/Swoole/Delegators/TCPServerDelegatorTest.php @@ -0,0 +1,101 @@ +markTestSkipped('Swoole extension not loaded.'); + } + + $logger = $this->createMock(LoggerInterface::class); + $bus = $this->createMock(MessageBusInterface::class); + + $server = new DummySwooleServer(); + $callback = fn (): Server => $server; + + $container = $this->createMock(ContainerInterface::class); + $container->method('get')->willReturnMap([ + [MessageBusInterface::class, $bus], + ['dot-log.queue-log', $logger], + ]); + + $delegator = new TCPServerDelegator(); + $result = $delegator($container, 'tcp-server', $callback); + + $this->assertContainsOnlyInstancesOf(Server::class, [$result]); + $this->assertArrayHasKey('Connect', $server->callbacks); + $this->assertArrayHasKey('receive', $server->callbacks); + $this->assertArrayHasKey('Close', $server->callbacks); + + foreach (['Connect', 'receive', 'Close'] as $event) { + $this->assertIsCallable($server->callbacks[$event]); + } + } + + /** + * @throws Exception + */ + #[RunInSeparateProcess] + public function testReceiveCallbackDispatchesMessagesAndLogs(): void + { + if (! \extension_loaded('swoole')) { + $this->markTestSkipped('Swoole extension not loaded.'); + } + + $dispatched = []; + + $bus = $this->createMock(MessageBusInterface::class); + $bus->method('dispatch')->willReturnCallback(function ($message) use (&$dispatched) { + $dispatched[] = $message; + return new Envelope($message); + }); + + $logger = $this->createMock(LoggerInterface::class); + $logger->expects($this->once()) + ->method('notice') + ->with( + $this->equalTo("Request received on receive"), + $this->arrayHasKey('fd') + ); + + $server = new DummySwooleServer(); + $callback = fn (): Server => $server; + + $container = $this->createMock(ContainerInterface::class); + $container->method('get')->willReturnMap([ + [MessageBusInterface::class, $bus], + ['dot-log.queue-log', $logger], + ]); + + $delegator = new TCPServerDelegator(); + $result = $delegator($container, 'tcp-server', $callback); + + $this->assertContainsOnlyInstancesOf(Server::class, [$result]); + + $receive = $server->callbacks['receive'] ?? null; + $this->assertIsCallable($receive); + + $receive($server, 1, 1, 'hello'); + + $this->assertCount(2, $dispatched); + } +} diff --git a/test/Swoole/Exception/InvalidStaticResourceMiddlewareExceptionTest.php b/test/Swoole/Exception/InvalidStaticResourceMiddlewareExceptionTest.php new file mode 100644 index 0000000..461edda --- /dev/null +++ b/test/Swoole/Exception/InvalidStaticResourceMiddlewareExceptionTest.php @@ -0,0 +1,32 @@ +assertContainsOnlyInstancesOf(InvalidStaticResourceMiddlewareException::class, [$exception]); + + $expectedMessage = sprintf( + 'Static resource middleware must be callable; received middleware of type "%s" in position %s', + get_debug_type($middleware), + $position + ); + + $this->assertSame($expectedMessage, $exception->getMessage()); + } +} diff --git a/test/Swoole/PidManagerFactoryTest.php b/test/Swoole/PidManagerFactoryTest.php new file mode 100644 index 0000000..b5e52db --- /dev/null +++ b/test/Swoole/PidManagerFactoryTest.php @@ -0,0 +1,54 @@ + [ + 'swoole-tcp-server' => [ + 'options' => [ + 'pid_file' => $expectedPath, + ], + ], + ], + ]; + + $container = $this->createMock(ContainerInterface::class); + $container->method('get') + ->with('config') + ->willReturn($config); + + $factory = new PidManagerFactory(); + $pidManager = $factory($container); + + $pidFilePath = $this->getPrivateProperty($pidManager); + $this->assertSame($expectedPath, $pidFilePath); + } + + /** + * @throws ReflectionException + */ + private function getPrivateProperty(object $object): mixed + { + $reflection = new \ReflectionClass($object); + $property = $reflection->getProperty('pidFile'); + return $property->getValue($object); + } +} diff --git a/test/Swoole/PidManagerTest.php b/test/Swoole/PidManagerTest.php new file mode 100644 index 0000000..f6c22e4 --- /dev/null +++ b/test/Swoole/PidManagerTest.php @@ -0,0 +1,92 @@ +tempPidFile = sys_get_temp_dir() . '/test.pid'; + if (file_exists($this->tempPidFile)) { + unlink($this->tempPidFile); + } + } + + protected function tearDown(): void + { + if (file_exists($this->tempPidFile)) { + unlink($this->tempPidFile); + } + } + + public function testWriteAndReadPids(): void + { + $manager = new PidManager($this->tempPidFile); + + $manager->write(12345, 67890); + + $result = $manager->read(); + + $this->assertSame(['12345', '67890'], $result); + $this->assertFileExists($this->tempPidFile); + } + + public function testDeleteRemovesPidFile(): void + { + file_put_contents($this->tempPidFile, 'dummyData'); + + $manager = new PidManager($this->tempPidFile); + $deleted = $manager->delete(); + + $this->assertTrue($deleted); + $this->assertFileDoesNotExist($this->tempPidFile); + } + + public function testDeleteReturnsFalseIfFileNotWritable(): void + { + file_put_contents($this->tempPidFile, 'dummyData'); + chmod($this->tempPidFile, 0444); + + $manager = new PidManager($this->tempPidFile); + $result = $manager->delete(); + + $this->assertFalse($result); + + chmod($this->tempPidFile, 0644); + } + + public function testWriteThrowsWhenFileNotWritable(): void + { + $unwritableDir = sys_get_temp_dir() . '/unwritable_dir'; + mkdir($unwritableDir, 0444); + $unwritableFile = $unwritableDir . '/file.pid'; + + $manager = new PidManager($unwritableFile); + + $this->expectException(RuntimeException::class); + $this->expectExceptionMessageMatches('/not writable/'); + + try { + $manager->write(1, 2); + } finally { + chmod($unwritableDir, 0755); + rmdir($unwritableDir); + } + } +} diff --git a/test/Swoole/ServerFactoryTest.php b/test/Swoole/ServerFactoryTest.php new file mode 100644 index 0000000..1f02d1f --- /dev/null +++ b/test/Swoole/ServerFactoryTest.php @@ -0,0 +1,153 @@ +factory = new ServerFactory(); + } + + /** + * @throws Exception + */ + #[RunInSeparateProcess] + public function testInvokeWithMinimalValidConfig(): void + { + if (! extension_loaded('swoole')) { + $this->markTestSkipped('Swoole extension not loaded.'); + } + + $config = [ + 'dotkernel-queue-swoole' => [ + 'swoole-tcp-server' => [], + ], + ]; + + $container = $this->createMock(ContainerInterface::class); + $container->method('get')->with('config')->willReturn($config); + + $server = $this->factory->__invoke($container); + + $this->assertContainsOnlyInstancesOf(Server::class, [$server]); + } + + /** + * @throws Exception + */ + #[RunInSeparateProcess] + public function testInvokeWithCustomValidConfig(): void + { + if (! extension_loaded('swoole')) { + $this->markTestSkipped('Swoole extension not loaded.'); + } + + $config = [ + 'dotkernel-queue-swoole' => [ + 'enable_coroutine' => true, + 'swoole-tcp-server' => [ + 'host' => '127.0.0.1', + 'port' => 9502, + 'mode' => SWOOLE_BASE, + 'protocol' => SWOOLE_SOCK_TCP, + 'options' => [ + 'worker_num' => 1, + ], + ], + ], + ]; + + $container = $this->createMock(ContainerInterface::class); + $container->method('get')->with('config')->willReturn($config); + + $server = $this->factory->__invoke($container); + + $this->assertContainsOnlyInstancesOf(Server::class, [$server]); + } + + /** + * @throws Exception + */ + public function testThrowsOnInvalidPort(): void + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('Invalid port'); + + $config = [ + 'dotkernel-queue-swoole' => [ + 'swoole-tcp-server' => [ + 'port' => 70000, + ], + ], + ]; + + $container = $this->createMock(ContainerInterface::class); + $container->method('get')->with('config')->willReturn($config); + + $this->factory->__invoke($container); + } + + /** + * @throws Exception + */ + public function testThrowsOnInvalidMode(): void + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('Invalid server mode'); + + $config = [ + 'dotkernel-queue-swoole' => [ + 'swoole-tcp-server' => [ + 'mode' => -1, + ], + ], + ]; + + $container = $this->createMock(ContainerInterface::class); + $container->method('get')->with('config')->willReturn($config); + + $this->factory->__invoke($container); + } + + /** + * @throws Exception + */ + public function testThrowsOnInvalidProtocol(): void + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('Invalid server protocol'); + + $config = [ + 'dotkernel-queue-swoole' => [ + 'swoole-tcp-server' => [ + 'protocol' => -99, + ], + ], + ]; + + $container = $this->createMock(ContainerInterface::class); + $container->method('get')->with('config')->willReturn($config); + + $this->factory->__invoke($container); + } +} diff --git a/test/SwooleConfigProviderTest.php b/test/Swoole/SwooleConfigProviderTest.php similarity index 93% rename from test/SwooleConfigProviderTest.php rename to test/Swoole/SwooleConfigProviderTest.php index 8647eb6..9c479ca 100644 --- a/test/SwooleConfigProviderTest.php +++ b/test/Swoole/SwooleConfigProviderTest.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace DotTest\Mail; +namespace QueueTest\Swoole; use PHPUnit\Framework\TestCase; use Queue\Swoole\ConfigProvider; From 59a7f4e784e8ebb40a74fce6de6fd8b83569dc4f Mon Sep 17 00:00:00 2001 From: bota Date: Mon, 14 Jul 2025 16:30:53 +0300 Subject: [PATCH 2/4] skip tests if swoole ext is missing Signed-off-by: bota --- .../Command/Factory/StopCommandFactoryTest.php | 4 ++++ test/Swoole/Command/StartCommandTest.php | 4 ++++ test/Swoole/Command/StopCommandTest.php | 12 ++++++++++++ test/Swoole/ServerFactoryTest.php | 12 ++++++++++++ 4 files changed, 32 insertions(+) diff --git a/test/Swoole/Command/Factory/StopCommandFactoryTest.php b/test/Swoole/Command/Factory/StopCommandFactoryTest.php index a2ca055..12f2431 100644 --- a/test/Swoole/Command/Factory/StopCommandFactoryTest.php +++ b/test/Swoole/Command/Factory/StopCommandFactoryTest.php @@ -18,6 +18,10 @@ class StopCommandFactoryTest extends TestCase */ public function testFactoryReturnsStopCommandInstance(): void { + if (! \extension_loaded('swoole')) { + $this->markTestSkipped('Swoole extension not loaded.'); + } + $pidManager = $this->createMock(PidManager::class); $container = $this->createMock(ContainerInterface::class); diff --git a/test/Swoole/Command/StartCommandTest.php b/test/Swoole/Command/StartCommandTest.php index 246976b..fab503e 100644 --- a/test/Swoole/Command/StartCommandTest.php +++ b/test/Swoole/Command/StartCommandTest.php @@ -22,6 +22,10 @@ class StartCommandTest extends TestCase */ public function testExecuteWhenServerIsNotRunning(): void { + if (! \extension_loaded('swoole')) { + $this->markTestSkipped('Swoole extension not loaded.'); + } + $input = $this->createMock(InputInterface::class); $output = $this->createMock(OutputInterface::class); $pidManager = $this->createMock(PidManager::class); diff --git a/test/Swoole/Command/StopCommandTest.php b/test/Swoole/Command/StopCommandTest.php index dbdcca2..58fe22c 100644 --- a/test/Swoole/Command/StopCommandTest.php +++ b/test/Swoole/Command/StopCommandTest.php @@ -17,6 +17,10 @@ class StopCommandTest extends TestCase */ public function testExecuteWhenServerIsNotRunning(): void { + if (! \extension_loaded('swoole')) { + $this->markTestSkipped('Swoole extension not loaded.'); + } + $pidManager = $this->createMock(PidManager::class); $command = $this->getMockBuilder(StopCommand::class) @@ -38,6 +42,10 @@ public function testExecuteWhenServerIsNotRunning(): void */ public function testExecuteWhenServerStopsSuccessfully(): void { + if (! \extension_loaded('swoole')) { + $this->markTestSkipped('Swoole extension not loaded.'); + } + $pidManager = $this->createMock(PidManager::class); $pidManager->method('read')->willReturn(['1234']); $pidManager->expects($this->once())->method('delete'); @@ -65,6 +73,10 @@ public function testExecuteWhenServerStopsSuccessfully(): void */ public function testExecuteWhenServerFailsToStop(): void { + if (! \extension_loaded('swoole')) { + $this->markTestSkipped('Swoole extension not loaded.'); + } + $pidManager = $this->createMock(PidManager::class); $pidManager->method('read')->willReturn(['1234']); $pidManager->expects($this->never())->method('delete'); diff --git a/test/Swoole/ServerFactoryTest.php b/test/Swoole/ServerFactoryTest.php index 1f02d1f..62f2fb0 100644 --- a/test/Swoole/ServerFactoryTest.php +++ b/test/Swoole/ServerFactoryTest.php @@ -90,6 +90,10 @@ public function testInvokeWithCustomValidConfig(): void */ public function testThrowsOnInvalidPort(): void { + if (! \extension_loaded('swoole')) { + $this->markTestSkipped('Swoole extension not loaded.'); + } + $this->expectException(InvalidArgumentException::class); $this->expectExceptionMessage('Invalid port'); @@ -112,6 +116,10 @@ public function testThrowsOnInvalidPort(): void */ public function testThrowsOnInvalidMode(): void { + if (! \extension_loaded('swoole')) { + $this->markTestSkipped('Swoole extension not loaded.'); + } + $this->expectException(InvalidArgumentException::class); $this->expectExceptionMessage('Invalid server mode'); @@ -134,6 +142,10 @@ public function testThrowsOnInvalidMode(): void */ public function testThrowsOnInvalidProtocol(): void { + if (! \extension_loaded('swoole')) { + $this->markTestSkipped('Swoole extension not loaded.'); + } + $this->expectException(InvalidArgumentException::class); $this->expectExceptionMessage('Invalid server protocol'); From d8aaccf943cc65c9f3eb1e0561e8daea0c706dea Mon Sep 17 00:00:00 2001 From: bota Date: Tue, 19 Aug 2025 13:29:05 +0300 Subject: [PATCH 3/4] fixed missing ext-redis Signed-off-by: bota --- .laminas-ci.json | 8 +- .laminas-ci/pre-run.sh | 8 +- config/autoload/mail.global.php | 86 +++++++++++++++++++ src/App/ConfigProvider.php | 10 +-- .../{ExampleMessage.php => Message.php} | 2 +- ...eMessageHandler.php => MessageHandler.php} | 4 +- src/Swoole/Delegators/TCPServerDelegator.php | 6 +- .../App/Message/ExampleMessageHandlerTest.php | 16 ++-- test/App/Message/ExampleMessageTest.php | 4 +- .../Factory/StopCommandFactoryTest.php | 4 - test/Swoole/Command/StartCommandTest.php | 4 - test/Swoole/Command/StopCommandTest.php | 12 --- .../Delegators/TCPServerDelegatorTest.php | 8 -- test/Swoole/ServerFactoryTest.php | 22 ----- 14 files changed, 116 insertions(+), 78 deletions(-) create mode 100644 config/autoload/mail.global.php rename src/App/Message/{ExampleMessage.php => Message.php} (91%) rename src/App/Message/{ExampleMessageHandler.php => MessageHandler.php} (96%) diff --git a/.laminas-ci.json b/.laminas-ci.json index 64be8c2..af5962f 100644 --- a/.laminas-ci.json +++ b/.laminas-ci.json @@ -1,8 +1,10 @@ { "additional_composer_arguments": [ - "--no-scripts", - "--no-plugins" + "--no-scripts" + ], + "extensions": [ + "redis" ], "ignore_php_platform_requirements": { } -} \ No newline at end of file +} diff --git a/.laminas-ci/pre-run.sh b/.laminas-ci/pre-run.sh index 8b8528d..b7ba4af 100755 --- a/.laminas-ci/pre-run.sh +++ b/.laminas-ci/pre-run.sh @@ -1,5 +1,5 @@ -#!/bin/bash +JOB=$3 +PHP_VERSION=$(echo "${JOB}" | jq -r '.php') -# Due to the fact that we are disabling plugins when installing/updating/downgrading composer dependencies -# we have to manually enable the coding standard here. -composer enable-codestandard +apt update +apt install -y "php${PHP_VERSION}-swoole" diff --git a/config/autoload/mail.global.php b/config/autoload/mail.global.php new file mode 100644 index 0000000..ad0711d --- /dev/null +++ b/config/autoload/mail.global.php @@ -0,0 +1,86 @@ + [ + //the key is the mail service name, this is the default one, which does not extend any configuration + 'default' => [ + //message configuration + 'message_options' => [ + //from email address of the email + 'from' => '', + //from name to be displayed instead of from address + 'from_name' => '', + //reply-to email address of the email + 'reply_to' => '', + //replyTo name to be displayed instead of the address + 'reply_to_name' => '', + //destination email address as string or a list of email addresses + 'to' => [], + //copy destination addresses + 'cc' => [], + //hidden copy destination addresses + 'bcc' => [], + //email subject + 'subject' => '', + //body options - content can be plain text, HTML + 'body' => [ + 'content' => '', + 'charset' => 'utf-8', + ], + //attachments config + 'attachments' => [ + 'files' => [], + 'dir' => [ + 'iterate' => false, + 'path' => 'data/mail/attachments', + 'recursive' => false, + ], + ], + ], + /** + * the mail transport to use can be any class implementing + * Symfony\Component\Mailer\Transport\TransportInterface + * + * for standard mail transports, you can use these aliases: + * - sendmail => Symfony\Component\Mailer\Transport\SendmailTransport + * - esmtp => Symfony\Component\Mailer\Transport\Smtp\EsmtpTransport + * + * defaults to sendmail + **/ + 'transport' => 'sendmail', + //options that will be used only if esmtp adapter is used + 'smtp_options' => [ + //hostname or IP address of the mail server + 'host' => '', + //port of the mail server - 587 or 465 for secure connections + 'port' => 587, + 'connection_config' => [ + //the smtp authentication identity + 'username' => '', + //the smtp authentication credential + 'password' => '', + /** + * tls will run by default on this component, use: + * + * null - to avoid interfering with automatic encryption + * false - to disable automatic encryption + * + * It's not recommended to disable TLS while connecting to an SMTP server over the Internet + **/ + 'tls' => null, + ], + ], + ], + // option to log the SENT emails + 'log' => [ + 'sent' => getcwd() . '/log/mail/sent.log', + ], + ], +]; diff --git a/src/App/ConfigProvider.php b/src/App/ConfigProvider.php index 9d6963d..882f20f 100644 --- a/src/App/ConfigProvider.php +++ b/src/App/ConfigProvider.php @@ -10,8 +10,8 @@ use Netglue\PsrContainer\Messenger\Container\Middleware\MessageHandlerMiddlewareStaticFactory; use Netglue\PsrContainer\Messenger\Container\Middleware\MessageSenderMiddlewareStaticFactory; use Netglue\PsrContainer\Messenger\HandlerLocator\OneToManyFqcnContainerHandlerLocator; -use Queue\App\Message\ExampleMessage; -use Queue\App\Message\ExampleMessageHandler; +use Queue\App\Message\Message; +use Queue\App\Message\MessageHandler; use Symfony\Component\Messenger\MessageBusInterface; class ConfigProvider @@ -37,7 +37,7 @@ private function getDependencies(): array "message_bus_stamp_middleware" => [BusNameStampMiddlewareStaticFactory::class, "message_bus"], "message_bus_sender_middleware" => [MessageSenderMiddlewareStaticFactory::class, "message_bus"], "message_bus_handler_middleware" => [MessageHandlerMiddlewareStaticFactory::class, "message_bus"], - ExampleMessageHandler::class => AttributedServiceFactory::class, + MessageHandler::class => AttributedServiceFactory::class, ], "aliases" => [ MessageBusInterface::class => "message_bus", @@ -81,7 +81,7 @@ private function busConfig(): array */ 'handler_locator' => OneToManyFqcnContainerHandlerLocator::class, 'handlers' => [ - ExampleMessage::class => [ExampleMessageHandler::class], + Message::class => [MessageHandler::class], ], /** @@ -97,7 +97,7 @@ private function busConfig(): array * Route specific messages to specific transports by using the message name as the key. */ 'routes' => [ - ExampleMessage::class => ["redis_transport"], + Message::class => ["redis_transport"], ], ], ]; diff --git a/src/App/Message/ExampleMessage.php b/src/App/Message/Message.php similarity index 91% rename from src/App/Message/ExampleMessage.php rename to src/App/Message/Message.php index d094f4d..aeee893 100644 --- a/src/App/Message/ExampleMessage.php +++ b/src/App/Message/Message.php @@ -4,7 +4,7 @@ namespace Queue\App\Message; -class ExampleMessage +class Message { public function __construct( private array $payload, diff --git a/src/App/Message/ExampleMessageHandler.php b/src/App/Message/MessageHandler.php similarity index 96% rename from src/App/Message/ExampleMessageHandler.php rename to src/App/Message/MessageHandler.php index 9f5a4b6..f96f8d9 100644 --- a/src/App/Message/ExampleMessageHandler.php +++ b/src/App/Message/MessageHandler.php @@ -15,7 +15,7 @@ use function json_decode; -class ExampleMessageHandler +class MessageHandler { protected array $args = []; @@ -35,7 +35,7 @@ public function __construct( ) { } - public function __invoke(ExampleMessage $message): void + public function __invoke(Message $message): void { $payload = json_decode($message->getPayload()['foo'], true); diff --git a/src/Swoole/Delegators/TCPServerDelegator.php b/src/Swoole/Delegators/TCPServerDelegator.php index a5e6a48..4302f43 100644 --- a/src/Swoole/Delegators/TCPServerDelegator.php +++ b/src/Swoole/Delegators/TCPServerDelegator.php @@ -5,7 +5,7 @@ namespace Queue\Swoole\Delegators; use Psr\Container\ContainerInterface; -use Queue\App\Message\ExampleMessage; +use Queue\App\Message\Message; use Swoole\Server as TCPSwooleServer; use Symfony\Component\Messenger\MessageBusInterface; use Symfony\Component\Messenger\Stamp\DelayStamp; @@ -28,8 +28,8 @@ public function __invoke(ContainerInterface $container, string $serviceName, cal // Register the function for the event `receive` $server->on('receive', function ($server, $fd, $fromId, $data) use ($logger, $bus) { - $bus->dispatch(new ExampleMessage(["foo" => $data])); - $bus->dispatch(new ExampleMessage(["foo" => "with 5 seconds delay"]), [ + $bus->dispatch(new Message(["foo" => $data])); + $bus->dispatch(new Message(["foo" => "with 5 seconds delay"]), [ new DelayStamp(5000), ]); diff --git a/test/App/Message/ExampleMessageHandlerTest.php b/test/App/Message/ExampleMessageHandlerTest.php index ba2d274..f956ab9 100644 --- a/test/App/Message/ExampleMessageHandlerTest.php +++ b/test/App/Message/ExampleMessageHandlerTest.php @@ -13,8 +13,8 @@ use Mezzio\Template\TemplateRendererInterface; use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; -use Queue\App\Message\ExampleMessage; -use Queue\App\Message\ExampleMessageHandler; +use Queue\App\Message\Message; +use Queue\App\Message\MessageHandler; use Symfony\Component\Mailer\Exception\TransportExceptionInterface; use function json_encode; @@ -26,7 +26,7 @@ class ExampleMessageHandlerTest extends TestCase protected UserRepository|MockObject $userRepository; protected Logger $logger; protected array $config; - private ExampleMessageHandler $handler; + private MessageHandler $handler; /** * @throws Exception|\PHPUnit\Framework\MockObject\Exception @@ -60,7 +60,7 @@ public function setUp(): void ], ]; - $this->handler = new ExampleMessageHandler( + $this->handler = new MessageHandler( $this->mailService, $this->renderer, $this->userRepository, @@ -76,12 +76,12 @@ public function testInvokeHandlesExceptionThrownByPerform(): void { $uuid = '1234'; - $message = $this->createMock(ExampleMessage::class); + $message = $this->createMock(Message::class); $message->method('getPayload')->willReturn([ 'foo' => json_encode(['userUuid' => $uuid]), ]); - $handlerMock = $this->getMockBuilder(ExampleMessageHandler::class) + $handlerMock = $this->getMockBuilder(MessageHandler::class) ->setConstructorArgs([ $this->mailService, $this->renderer, @@ -109,7 +109,7 @@ public function testInvokeWithValidPayload(): void $email = 'test@dotkernel.com'; $name = 'dotkernel'; - $message = $this->createMock(ExampleMessage::class); + $message = $this->createMock(Message::class); $message->method('getPayload')->willReturn([ 'foo' => json_encode(['userUuid' => $uuid]), ]); @@ -187,7 +187,7 @@ public function testSendWelcomeMailHandlesMailException(): void */ public function testInvokeWithInvalidJsonSkipsPerform(): void { - $message = $this->createMock(ExampleMessage::class); + $message = $this->createMock(Message::class); $message->method('getPayload')->willReturn([ 'foo' => '{"userUuid":', ]); diff --git a/test/App/Message/ExampleMessageTest.php b/test/App/Message/ExampleMessageTest.php index 7f00eaa..15cbb36 100644 --- a/test/App/Message/ExampleMessageTest.php +++ b/test/App/Message/ExampleMessageTest.php @@ -5,13 +5,13 @@ namespace QueueTest\App\Message; use PHPUnit\Framework\TestCase; -use Queue\App\Message\ExampleMessage; +use Queue\App\Message\Message; class ExampleMessageTest extends TestCase { public function testMessageAccessors(): void { - $admin = new ExampleMessage(["payload" => "test message payload"]); + $admin = new Message(["payload" => "test message payload"]); $this->assertSame(["payload" => "test message payload"], $admin->getPayload()); } } diff --git a/test/Swoole/Command/Factory/StopCommandFactoryTest.php b/test/Swoole/Command/Factory/StopCommandFactoryTest.php index 12f2431..a2ca055 100644 --- a/test/Swoole/Command/Factory/StopCommandFactoryTest.php +++ b/test/Swoole/Command/Factory/StopCommandFactoryTest.php @@ -18,10 +18,6 @@ class StopCommandFactoryTest extends TestCase */ public function testFactoryReturnsStopCommandInstance(): void { - if (! \extension_loaded('swoole')) { - $this->markTestSkipped('Swoole extension not loaded.'); - } - $pidManager = $this->createMock(PidManager::class); $container = $this->createMock(ContainerInterface::class); diff --git a/test/Swoole/Command/StartCommandTest.php b/test/Swoole/Command/StartCommandTest.php index fab503e..246976b 100644 --- a/test/Swoole/Command/StartCommandTest.php +++ b/test/Swoole/Command/StartCommandTest.php @@ -22,10 +22,6 @@ class StartCommandTest extends TestCase */ public function testExecuteWhenServerIsNotRunning(): void { - if (! \extension_loaded('swoole')) { - $this->markTestSkipped('Swoole extension not loaded.'); - } - $input = $this->createMock(InputInterface::class); $output = $this->createMock(OutputInterface::class); $pidManager = $this->createMock(PidManager::class); diff --git a/test/Swoole/Command/StopCommandTest.php b/test/Swoole/Command/StopCommandTest.php index 58fe22c..dbdcca2 100644 --- a/test/Swoole/Command/StopCommandTest.php +++ b/test/Swoole/Command/StopCommandTest.php @@ -17,10 +17,6 @@ class StopCommandTest extends TestCase */ public function testExecuteWhenServerIsNotRunning(): void { - if (! \extension_loaded('swoole')) { - $this->markTestSkipped('Swoole extension not loaded.'); - } - $pidManager = $this->createMock(PidManager::class); $command = $this->getMockBuilder(StopCommand::class) @@ -42,10 +38,6 @@ public function testExecuteWhenServerIsNotRunning(): void */ public function testExecuteWhenServerStopsSuccessfully(): void { - if (! \extension_loaded('swoole')) { - $this->markTestSkipped('Swoole extension not loaded.'); - } - $pidManager = $this->createMock(PidManager::class); $pidManager->method('read')->willReturn(['1234']); $pidManager->expects($this->once())->method('delete'); @@ -73,10 +65,6 @@ public function testExecuteWhenServerStopsSuccessfully(): void */ public function testExecuteWhenServerFailsToStop(): void { - if (! \extension_loaded('swoole')) { - $this->markTestSkipped('Swoole extension not loaded.'); - } - $pidManager = $this->createMock(PidManager::class); $pidManager->method('read')->willReturn(['1234']); $pidManager->expects($this->never())->method('delete'); diff --git a/test/Swoole/Delegators/TCPServerDelegatorTest.php b/test/Swoole/Delegators/TCPServerDelegatorTest.php index 78fb8df..6ff7953 100644 --- a/test/Swoole/Delegators/TCPServerDelegatorTest.php +++ b/test/Swoole/Delegators/TCPServerDelegatorTest.php @@ -22,10 +22,6 @@ class TCPServerDelegatorTest extends TestCase #[RunInSeparateProcess] public function testInvokeRegistersAllCallbacks(): void { - if (! \extension_loaded('swoole')) { - $this->markTestSkipped('Swoole extension not loaded.'); - } - $logger = $this->createMock(LoggerInterface::class); $bus = $this->createMock(MessageBusInterface::class); @@ -57,10 +53,6 @@ public function testInvokeRegistersAllCallbacks(): void #[RunInSeparateProcess] public function testReceiveCallbackDispatchesMessagesAndLogs(): void { - if (! \extension_loaded('swoole')) { - $this->markTestSkipped('Swoole extension not loaded.'); - } - $dispatched = []; $bus = $this->createMock(MessageBusInterface::class); diff --git a/test/Swoole/ServerFactoryTest.php b/test/Swoole/ServerFactoryTest.php index 62f2fb0..007ea40 100644 --- a/test/Swoole/ServerFactoryTest.php +++ b/test/Swoole/ServerFactoryTest.php @@ -12,8 +12,6 @@ use Queue\Swoole\ServerFactory; use Swoole\Server; -use function extension_loaded; - use const SWOOLE_BASE; use const SWOOLE_SOCK_TCP; @@ -34,10 +32,6 @@ protected function setUp(): void #[RunInSeparateProcess] public function testInvokeWithMinimalValidConfig(): void { - if (! extension_loaded('swoole')) { - $this->markTestSkipped('Swoole extension not loaded.'); - } - $config = [ 'dotkernel-queue-swoole' => [ 'swoole-tcp-server' => [], @@ -58,10 +52,6 @@ public function testInvokeWithMinimalValidConfig(): void #[RunInSeparateProcess] public function testInvokeWithCustomValidConfig(): void { - if (! extension_loaded('swoole')) { - $this->markTestSkipped('Swoole extension not loaded.'); - } - $config = [ 'dotkernel-queue-swoole' => [ 'enable_coroutine' => true, @@ -90,10 +80,6 @@ public function testInvokeWithCustomValidConfig(): void */ public function testThrowsOnInvalidPort(): void { - if (! \extension_loaded('swoole')) { - $this->markTestSkipped('Swoole extension not loaded.'); - } - $this->expectException(InvalidArgumentException::class); $this->expectExceptionMessage('Invalid port'); @@ -116,10 +102,6 @@ public function testThrowsOnInvalidPort(): void */ public function testThrowsOnInvalidMode(): void { - if (! \extension_loaded('swoole')) { - $this->markTestSkipped('Swoole extension not loaded.'); - } - $this->expectException(InvalidArgumentException::class); $this->expectExceptionMessage('Invalid server mode'); @@ -142,10 +124,6 @@ public function testThrowsOnInvalidMode(): void */ public function testThrowsOnInvalidProtocol(): void { - if (! \extension_loaded('swoole')) { - $this->markTestSkipped('Swoole extension not loaded.'); - } - $this->expectException(InvalidArgumentException::class); $this->expectExceptionMessage('Invalid server protocol'); From aae880f8728d55644cb7c1eb6235d41d72cac891 Mon Sep 17 00:00:00 2001 From: bota Date: Tue, 19 Aug 2025 13:32:50 +0300 Subject: [PATCH 4/4] added swoole extension to codecov Signed-off-by: bota --- .github/workflows/codecov.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/codecov.yml b/.github/workflows/codecov.yml index c1c41c3..a01d989 100644 --- a/.github/workflows/codecov.yml +++ b/.github/workflows/codecov.yml @@ -26,6 +26,7 @@ jobs: uses: shivammathur/setup-php@v2 with: php-version: "${{ matrix.php }}" + extensions: swoole coverage: pcov ini-values: assert.exception=1, zend.assertions=1, error_reporting=-1, log_errors_max_len=0, display_errors=On tools: composer:v2, cs2pr