diff --git a/src/WebSocket/Adapter/Swoole.php b/src/WebSocket/Adapter/Swoole.php index c39efe7..3d51810 100644 --- a/src/WebSocket/Adapter/Swoole.php +++ b/src/WebSocket/Adapter/Swoole.php @@ -109,6 +109,9 @@ public function onOpen(callable $callback): self public function onMessage(callable $callback): self { $this->server->on('message', function (Server $server, Frame $frame) use ($callback) { + if ($frame->data === '' || $frame->data === null) { + return; + } call_user_func($callback, $frame->fd, $frame->data); }); diff --git a/tests/unit/SwooleAdapterTest.php b/tests/unit/SwooleAdapterTest.php new file mode 100644 index 0000000..d23dfc7 --- /dev/null +++ b/tests/unit/SwooleAdapterTest.php @@ -0,0 +1,99 @@ +getMockBuilder(Swoole::class) + ->disableOriginalConstructor() + ->onlyMethods([]) + ->getMock(); + + $mockServer = $this->getMockBuilder(Server::class) + ->disableOriginalConstructor() + ->getMock(); + } catch (\Error $e) { + if (strpos($e->getMessage(), 'enum_exists') !== false) { + $this->markTestSkipped('Test skipped due to enum_exists compatibility issue'); + } + throw $e; + } + + $reflection = new \ReflectionClass(Swoole::class); + $serverProperty = $reflection->getProperty('server'); + $serverProperty->setAccessible(true); + $serverProperty->setValue($adapter, $mockServer); + + return [$adapter, $mockServer]; + } + + public function testOnMessageSkipsEmptyData(): void + { + [$adapter, $mockServer] = $this->createAdapterWithMockServer(); + + $registeredCallback = null; + $mockServer->expects($this->once()) + ->method('on') + ->with('message', $this->callback(function ($callback) use (&$registeredCallback) { + $registeredCallback = $callback; + return true; + })); + + $callbackInvoked = false; + $adapter->onMessage(function (int $connection, string $message) use (&$callbackInvoked) { + $callbackInvoked = true; + }); + + $this->assertNotNull($registeredCallback, 'Server on() should have been called'); + + // Simulate a frame with empty data + $frame = new Frame(); + $frame->fd = 1; + $frame->data = ''; + + $registeredCallback($mockServer, $frame); + + $this->assertFalse($callbackInvoked, 'Callback should not be invoked for empty message data'); + } + + public function testOnMessageCallsCallbackWithValidData(): void + { + [$adapter, $mockServer] = $this->createAdapterWithMockServer(); + + $registeredCallback = null; + $mockServer->expects($this->once()) + ->method('on') + ->with('message', $this->callback(function ($callback) use (&$registeredCallback) { + $registeredCallback = $callback; + return true; + })); + + $receivedFd = null; + $receivedData = null; + $adapter->onMessage(function (int $connection, string $message) use (&$receivedFd, &$receivedData) { + $receivedFd = $connection; + $receivedData = $message; + }); + + // Simulate a frame with valid data + $frame = new Frame(); + $frame->fd = 42; + $frame->data = '{"type":"authentication"}'; + + $registeredCallback($mockServer, $frame); + + $this->assertEquals(42, $receivedFd); + $this->assertEquals('{"type":"authentication"}', $receivedData); + } +}