diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 4f606d1..094df1c 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -17,8 +17,15 @@ jobs: - 8.1 - 8.0 - 7.4 + redis: + - "7" + - "6" + - "5" steps: - uses: actions/checkout@v3 + - uses: supercharge/redis-github-action@1.5.0 + with: + redis-version: ${{ matrix.redis }} - uses: shivammathur/setup-php@v2 with: php-version: ${{ matrix.php }} diff --git a/src/HookServer.php b/src/HookServer.php index 107500c..4eb7462 100644 --- a/src/HookServer.php +++ b/src/HookServer.php @@ -48,18 +48,14 @@ class HookServer implements ServerInterface /** @inheritDoc */ public static function getConfig(string $key, $default = null) { - return Server::isDebug() ? - config('plugin.workbunny.webman-push-server.app.hook-server.' . $key, $default) : - \config('plugin.workbunny.webman-push-server.app.hook-server.' . $key, $default); + return config('plugin.workbunny.webman-push-server.app.hook-server.' . $key, $default); } /** @inheritDoc */ public static function getStorage(): \Redis { if(!self::$_storage instanceof \Redis){ - self::$_storage = Server::isDebug() ? - new MockRedisStream() : - Redis::connection(self::getConfig('redis_channel', 'default'))->client(); + self::$_storage = Redis::connection(self::getConfig('redis_channel', 'default'))->client(); } return self::$_storage; } diff --git a/src/Server.php b/src/Server.php index 33f24ae..c8b1cf2 100644 --- a/src/Server.php +++ b/src/Server.php @@ -17,7 +17,6 @@ use RedisException; use support\Container; use support\Redis; -use Tests\MockClass\MockRedis; use Workbunny\WebmanPushServer\Events\AbstractEvent; use Workbunny\WebmanPushServer\Events\Unsubscribe; use Workerman\Connection\TcpConnection; @@ -184,17 +183,16 @@ public static function getServer(): ?Server /** @inheritDoc */ public static function getConfig(string $key, $default = null) { - return self::isDebug() ? - config('plugin.workbunny.webman-push-server.app.push-server.' . $key, $default) : - \config('plugin.workbunny.webman-push-server.app.push-server.' . $key, $default); + return \config( + 'plugin.workbunny.webman-push-server.app.push-server.' . $key, $default + ); } /** @inheritDoc */ public static function getStorage(): \Redis { if(!self::$_storage instanceof \Redis){ - self::$_storage = self::isDebug() ? - new MockRedis() : + self::$_storage = Redis::connection(self::getConfig('redis_channel', 'default'))->client(); } return self::$_storage; diff --git a/src/config/plugin/workbunny/webman-push-server/route.php b/src/config/plugin/workbunny/webman-push-server/route.php index 22b344d..015c312 100644 --- a/src/config/plugin/workbunny/webman-push-server/route.php +++ b/src/config/plugin/workbunny/webman-push-server/route.php @@ -150,7 +150,7 @@ $channels[$channel] = Server::getStorage()->hMGet($key, $fields) ?? []; } return response(200, ['channels' => $channels]); - }catch (\Throwable $throwable){ + } catch (\Throwable $throwable) { //TODO log return response(500, 'Server Error [Channels]'); } diff --git a/src/helpers.php b/src/helpers.php index 5fac14b..ab1fc47 100644 --- a/src/helpers.php +++ b/src/helpers.php @@ -32,19 +32,6 @@ function response(int $httpStatus, $data, array $header = []): Response } } -if (!function_exists('config')){ - /** - * @param string|null $key - * @param $default - * @return array|mixed|null - */ - function config(string $key = null, $default = null) - { - Config::load(__DIR__ . '/config', ['route']); - return Config::get($key, $default); - } -} - /** * 生成UUID */ @@ -80,4 +67,4 @@ function fuuid(): string $uuid .= substr($chars, 20, 12); return $uuid; } -} +} \ No newline at end of file diff --git a/tests/ApiServiceRouteHandlerTest.php b/tests/ApiServiceRouteHandlerTest.php index d4b5306..9ed20a0 100644 --- a/tests/ApiServiceRouteHandlerTest.php +++ b/tests/ApiServiceRouteHandlerTest.php @@ -22,6 +22,7 @@ class ApiServiceRouteHandlerTest extends BaseTestCase { + /** * 测试消息事件处理 * @covers \Workbunny\WebmanPushServer\ApiService::onMessage @@ -30,7 +31,6 @@ class ApiServiceRouteHandlerTest extends BaseTestCase */ public function testApiServiceChannels(){ $this->setServer(true); - Server::setServer($this->getServer()); // required auth_key @@ -82,13 +82,14 @@ public function testApiServiceChannels(){ */ public function testApiServiceChannel(){ $this->setServer(true); + Server::setServer($this->getServer()); $mockConnection = new MockTcpConnection(); $request = new Http\Request("GET /apps/1/channels/private-test?auth_key=workbunny&auth_signature=test HTTP/1.1\r\nConnection: keep-alive\r\n"); // 手动触发 onMessage 回调 Server::getServices(ApiService::class)->onMessage($mockConnection, $request); $this->assertTrue($mockConnection->getSendBuffer() instanceof Response); - $this->assertEquals('{"occupied":true,"type":null}', $mockConnection->getSendBuffer()->rawBody()); + $this->assertEquals('{"occupied":true,"type":false}', $mockConnection->getSendBuffer()->rawBody()); $this->assertEquals(200, $mockConnection->getSendBuffer()->getStatusCode()); $this->assertEquals('application/json', $mockConnection->getSendBuffer()->getHeader('Content-Type')); @@ -97,7 +98,7 @@ public function testApiServiceChannel(){ // 手动触发 onMessage 回调 Server::getServices(ApiService::class)->onMessage($mockConnection, $request); $this->assertTrue($mockConnection->getSendBuffer() instanceof Response); - $this->assertEquals('{"occupied":true,"type":null,"subscription_count":null}', $mockConnection->getSendBuffer()->rawBody()); + $this->assertEquals('{"occupied":true,"type":false,"subscription_count":false}', $mockConnection->getSendBuffer()->rawBody()); $this->assertEquals(200, $mockConnection->getSendBuffer()->getStatusCode()); $this->assertEquals('application/json', $mockConnection->getSendBuffer()->getHeader('Content-Type')); @@ -106,7 +107,7 @@ public function testApiServiceChannel(){ // 手动触发 onMessage 回调 Server::getServices(ApiService::class)->onMessage($mockConnection, $request); $this->assertTrue($mockConnection->getSendBuffer() instanceof Response); - $this->assertEquals('{"occupied":true,"type":null,"subscription_count":null,"user_count":null}', $mockConnection->getSendBuffer()->rawBody()); + $this->assertEquals('{"occupied":true,"type":false,"subscription_count":false,"user_count":false}', $mockConnection->getSendBuffer()->rawBody()); $this->assertEquals(200, $mockConnection->getSendBuffer()->getStatusCode()); $this->assertEquals('application/json', $mockConnection->getSendBuffer()->getHeader('Content-Type')); } @@ -119,6 +120,7 @@ public function testApiServiceChannel(){ */ public function testApiServiceEvents(){ $this->setServer(true); + Server::setServer($this->getServer()); $mockConnection = new MockTcpConnection(); $request = new Http\Request("POST /apps/1/events?auth_key=workbunny&auth_signature=test HTTP/1.1\r\nConnection: keep-alive\r\n"); @@ -140,6 +142,7 @@ public function testApiServiceEvents(){ */ public function testApiServiceBatchEvents(){ $this->setServer(true); + Server::setServer($this->getServer()); $mockConnection = new MockTcpConnection(); $request = new Http\Request("POST /apps/1/batch_events?auth_key=workbunny&auth_signature=test HTTP/1.1\r\nConnection: keep-alive\r\n"); @@ -161,6 +164,7 @@ public function testApiServiceBatchEvents(){ */ public function testApiServiceUsers(){ $this->setServer(true); + Server::setServer($this->getServer()); $mockConnection = new MockTcpConnection(); $request = new Http\Request("GET /apps/1/channels/private-test/users?auth_key=workbunny&auth_signature=test HTTP/1.1\r\nConnection: keep-alive\r\n"); @@ -180,6 +184,7 @@ public function testApiServiceUsers(){ */ public function testApiServiceTerminateConnections(){ $this->setServer(true); + Server::setServer($this->getServer()); $mockConnection = new MockTcpConnection(); $request = new Http\Request("POST /apps/1/users/abc/terminate_connections?auth_key=workbunny&auth_signature=test HTTP/1.1\r\nConnection: keep-alive\r\n"); diff --git a/tests/BaseTestCase.php b/tests/BaseTestCase.php index e46f27a..31e9d6f 100644 --- a/tests/BaseTestCase.php +++ b/tests/BaseTestCase.php @@ -15,6 +15,8 @@ use Exception; use PHPUnit\Framework\TestCase; +use support\Redis; +use Webman\Config; use Workbunny\WebmanPushServer\ApiService; use Workbunny\WebmanPushServer\Server; @@ -64,7 +66,8 @@ protected function setServer(bool $clean = false): void protected function setUp(): void { Server::$debug = true; - require_once dirname(__DIR__) . '/vendor/workerman/webman-framework/src/support/helpers.php'; + require_once __DIR__ . '/debug.php'; + Config::load(\config_path(), ['route']); parent::setUp(); } } diff --git a/tests/MockClass/MockRedis.php b/tests/MockClass/MockRedis.php index 7cf568c..08d1b07 100644 --- a/tests/MockClass/MockRedis.php +++ b/tests/MockClass/MockRedis.php @@ -74,14 +74,14 @@ public function hGet($key, $hashKey) return $this->_storage[$key][$hashKey] ?? null; } - public function del($key1, ...$otherKeys) + public function del($key, ...$otherKeys): \Redis|int|bool { - if($this->exists($key1)){ - unset($this->_storage[$key1]); + if($this->exists($key)){ + unset($this->_storage[$key]); } - foreach ($otherKeys as $key){ - if($this->exists($key)){ - unset($this->_storage[$key]); + foreach ($otherKeys as $k){ + if($this->exists($k)){ + unset($this->_storage[$k]); } } } diff --git a/tests/ServerBaseTest.php b/tests/ServerBaseTest.php index 9fe0317..c7024ac 100644 --- a/tests/ServerBaseTest.php +++ b/tests/ServerBaseTest.php @@ -19,6 +19,7 @@ use Tests\MockClass\MockTcpConnection; use Workbunny\WebmanPushServer\ApiService; use Workbunny\WebmanPushServer\HookServer; +use Workbunny\WebmanPushServer\Server; use Workbunny\WebmanPushServer\ServerInterface; class ServerBaseTest extends BaseTestCase @@ -46,6 +47,7 @@ public function testServerInit(){ */ public function testServerOnMessageWithNormalStringData(){ $this->setServer(true); + Server::setServer($this->getServer()); // normal string $mockConnection = new MockTcpConnection(); @@ -89,6 +91,7 @@ public function testServerOnMessageWithNormalStringData(){ public function testServerOnConnectSuccessful() { $this->setServer(true); + Server::setServer($this->getServer()); $mockConnection = new MockTcpConnection(); @@ -107,12 +110,15 @@ public function testServerOnConnectSuccessful() $this->assertEquals(['' => ''], $this->getServer()->_getConnectionProperty($mockConnection, 'channels')); $this->assertEquals($mockConnection, $this->getServer()->_getConnection($mockConnection, $appKey, '')); - /** @var MockRedisStream $storage */ $storage = HookServer::getStorage(); // 队列新增一条数据 - $this->assertCount(1, $queue = $storage->getStreams()['workbunny:webman-push-server:webhook-stream'] ?? []); + $this->assertEquals(1, $storage->exists('workbunny:webman-push-server:webhook-stream')); // 队列包含一条server_event事件 - $this->assertContains('server_event', array_column($queue, 'name')); + $this->assertContains('server_event', array_column($storage->xRead([ + 'workbunny:webman-push-server:webhook-stream' => '0-0' + ], -1, 1)['workbunny:webman-push-server:webhook-stream'] ?? [], 'name')); + // 移除队列 + $storage->del('workbunny:webman-push-server:webhook-stream'); } /** @@ -128,6 +134,7 @@ public function testServerOnConnectSuccessful() public function testServerOnConnectInvalidAppError() { $this->setServer(true); + Server::setServer($this->getServer()); // 无效的header $mockConnection = new MockTcpConnection(); @@ -140,6 +147,15 @@ public function testServerOnConnectInvalidAppError() $this->assertEquals(0, $this->getServer()->_getConnectionProperty($mockConnection, 'clientNotSendPingCount')); $this->assertTrue($mockConnection->isPaused()); $this->assertEquals('{"event":"pusher:error","data":{"code":null,"message":"Invalid app"}}', $mockConnection->getSendBuffer()); + $storage = HookServer::getStorage(); + // 队列新增一条数据 + $this->assertEquals(1, $storage->exists('workbunny:webman-push-server:webhook-stream')); + // 队列包含一条server_event事件 + $this->assertContains('server_event', array_column($storage->xRead([ + 'workbunny:webman-push-server:webhook-stream' => '0-0' + ], -1, 1)['workbunny:webman-push-server:webhook-stream'] ?? [], 'name')); + // 移除队列 + $storage->del('workbunny:webman-push-server:webhook-stream'); } /** @@ -155,6 +171,7 @@ public function testServerOnConnectInvalidAppError() public function testServerOnConnectInvalidAppKeyError() { $this->setServer(true); + Server::setServer($this->getServer()); // 无效的akk_key $mockConnection = new MockTcpConnection(); @@ -178,6 +195,7 @@ public function testServerOnConnectInvalidAppKeyError() */ public function testServerOnMessageWithBoolData(){ $this->setServer(true); + Server::setServer($this->getServer()); // bool $mockConnection = new MockTcpConnection(); @@ -196,6 +214,7 @@ public function testServerOnMessageWithBoolData(){ */ public function testServerOnMessageWithArrayData(){ $this->setServer(true); + Server::setServer($this->getServer()); // array $mockConnection = new MockTcpConnection(); @@ -214,6 +233,7 @@ public function testServerOnMessageWithArrayData(){ */ public function testServerOnMessageWithObjectData(){ $this->setServer(true); + Server::setServer($this->getServer()); // object $mockConnection = new MockTcpConnection(); diff --git a/tests/ServerEventHandlerTest.php b/tests/ServerEventHandlerTest.php index 961b751..a6acc35 100644 --- a/tests/ServerEventHandlerTest.php +++ b/tests/ServerEventHandlerTest.php @@ -19,6 +19,7 @@ use Workbunny\WebmanPushServer\Events\Ping; use Workbunny\WebmanPushServer\Events\Subscribe; use Workbunny\WebmanPushServer\Events\Unsubscribe; +use Workbunny\WebmanPushServer\HookServer; use Workbunny\WebmanPushServer\Server; use const Workbunny\WebmanPushServer\EVENT_MEMBER_REMOVED; use const Workbunny\WebmanPushServer\EVENT_PING; @@ -37,6 +38,7 @@ class ServerEventHandlerTest extends BaseTestCase */ public function testServerEventHandlerByPing(){ $this->setServer(true); + Server::setServer($this->getServer()); $mockConnection = new MockTcpConnection(); @@ -54,6 +56,15 @@ public function testServerEventHandlerByPing(){ $this->assertEquals(0, $this->getServer()->_getConnectionProperty($mockConnection, 'clientNotSendPingCount')); $this->assertTrue(Server::$eventFactory instanceof Ping); $this->assertEquals('{"event":"pusher:pong","data":{}}', $mockConnection->getSendBuffer()); + $storage = HookServer::getStorage(); + // 队列新增一条数据 + $this->assertEquals(1, $storage->exists('workbunny:webman-push-server:webhook-stream')); + // 队列包含一条server_event事件 + $this->assertContains('server_event', array_column($storage->xRead([ + 'workbunny:webman-push-server:webhook-stream' => '0-0' + ], -1, 1)['workbunny:webman-push-server:webhook-stream'] ?? [], 'name')); + // 移除队列 + $storage->del('workbunny:webman-push-server:webhook-stream'); } /** @@ -65,6 +76,7 @@ public function testServerEventHandlerByPing(){ */ public function testServerEventHandlerBySubscribe(){ $this->setServer(true); + Server::setServer($this->getServer()); $mockConnection = new MockTcpConnection(); // 手动触发 onConnect 回调 @@ -94,6 +106,7 @@ public function testServerEventHandlerBySubscribe(){ */ public function testServerEventHandlerByUnsubscribe(){ $this->setServer(true); + Server::setServer($this->getServer()); $mockConnection = new MockTcpConnection(); // 手动触发 onConnect 回调 @@ -123,6 +136,7 @@ public function testServerEventHandlerByUnsubscribe(){ */ public function testServerEventHandlerByCustomClientEvent(){ $this->setServer(true); + Server::setServer($this->getServer()); $mockConnection = new MockTcpConnection(); // 手动触发 onConnect 回调 @@ -146,6 +160,15 @@ public function testServerEventHandlerByCustomClientEvent(){ '{"event":"pusher:error","data":{"code":null,"message":"Client event rejected - only supported on private and presence channels"}}', $mockConnection->getSendBuffer() ); + $storage = HookServer::getStorage(); + // 队列新增一条数据 + $this->assertEquals(1, $storage->exists('workbunny:webman-push-server:webhook-stream')); + // 队列包含一条server_event事件 + $this->assertContains('server_event', array_column($storage->xRead([ + 'workbunny:webman-push-server:webhook-stream' => '0-0' + ], -1, 1)['workbunny:webman-push-server:webhook-stream'] ?? [], 'name')); + // 移除队列 + $storage->del('workbunny:webman-push-server:webhook-stream'); } /** @@ -157,6 +180,7 @@ public function testServerEventHandlerByCustomClientEvent(){ */ public function testServerEventHandlerByServerReturnClientEvent(){ $this->setServer(true); + Server::setServer($this->getServer()); // server return client-event $mockConnection = new MockTcpConnection(); @@ -191,5 +215,14 @@ public function testServerEventHandlerByServerReturnClientEvent(){ $this->assertEquals(0, $this->getServer()->_getConnectionProperty($mockConnection, 'clientNotSendPingCount')); $this->assertEquals(null, Server::$eventFactory); $this->assertEquals('{"event":"pusher:error","data":{"code":null,"message":"Client event rejected - Unknown event"}}', $mockConnection->getSendBuffer()); + $storage = HookServer::getStorage(); + // 队列新增一条数据 + $this->assertEquals(1, $storage->exists('workbunny:webman-push-server:webhook-stream')); + // 队列包含一条server_event事件 + $this->assertContains('server_event', array_column($storage->xRead([ + 'workbunny:webman-push-server:webhook-stream' => '0-0' + ], -1, 1)['workbunny:webman-push-server:webhook-stream'] ?? [], 'name')); + // 移除队列 + $storage->del('workbunny:webman-push-server:webhook-stream'); } } diff --git a/tests/debug.php b/tests/debug.php new file mode 100644 index 0000000..7d87a69 --- /dev/null +++ b/tests/debug.php @@ -0,0 +1,63 @@ + [ + 'host' => '172.17.0.1',//'redis', + 'password' => '', + 'port' => 6379, + 'database' => 0, + ], + ]; + } + return Config::get($key, $default); +} + +/** + * 测试用config path + * @return string + */ +function config_path(): string +{ + return dirname(__DIR__) . '/src/config'; +} + +/** + * 测试用runtime path + * @return string + */ +function runtime_path(): string +{ + return __DIR__ . '/runtime'; +} + +/** + * 测试用cpu count + * @return int + */ +function cpu_count(): int +{ + // Windows does not support the number of processes setting. + if (DIRECTORY_SEPARATOR === '\\') { + return 1; + } + $count = 4; + if (is_callable('shell_exec')) { + if (strtolower(PHP_OS) === 'darwin') { + $count = (int)shell_exec('sysctl -n machdep.cpu.core_count'); + } else { + $count = (int)shell_exec('nproc'); + } + } + return $count > 0 ? $count : 4; +}