Merge pull request #482 from beyondcode/code-coverage

[2.x] Code coverage fixes
This commit is contained in:
rennokki 2020-08-24 09:46:26 +03:00 committed by GitHub
commit 793dc24c24
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
39 changed files with 708 additions and 206 deletions

18
.codecov.yml Normal file
View File

@ -0,0 +1,18 @@
codecov:
notify:
require_ci_to_pass: yes
coverage:
precision: 2
round: down
range: "70...100"
status:
project: yes
patch: yes
changes: no
comment:
layout: "reach, diff, flags, files, footer"
behavior: default
require_changes: no

View File

@ -45,7 +45,7 @@ jobs:
- name: Install dependencies
run: |
composer require "laravel/framework:${{ matrix.laravel }}" "orchestra/testbench:${{ matrix.testbench }}" --no-interaction --no-update
composer require "laravel/framework:${{ matrix.laravel }}" "orchestra/testbench-browser-kit:${{ matrix.testbench }}" --no-interaction --no-update
composer update --${{ matrix.dependency-version }} --prefer-dist --no-interaction --no-suggest
- name: Execute tests with Local driver

1
.gitignore vendored
View File

@ -4,3 +4,4 @@ vendor
coverage
.phpunit.result.cache
.idea/
database.sqlite

View File

@ -42,7 +42,7 @@
},
"require-dev": {
"mockery/mockery": "^1.3",
"orchestra/testbench": "3.8.*|^4.0|^5.0",
"orchestra/testbench-browser-kit": "^4.0|^5.0",
"phpunit/phpunit": "^8.0|^9.0"
},
"autoload": {

View File

@ -0,0 +1,32 @@
<?php
namespace BeyondCode\LaravelWebSockets\Contracts;
use BeyondCode\LaravelWebSockets\PubSub\Broadcasters\RedisPusherBroadcaster;
use Illuminate\Broadcasting\Broadcasters\PusherBroadcaster;
use Pusher\Pusher;
trait PushesToPusher
{
/**
* Get the right Pusher broadcaster for the used driver.
*
* @param array $app
* @return \Illuminate\Broadcasting\Broadcasters\Broadcaster
*/
public function getPusherBroadcaster(array $app)
{
if (config('websockets.replication.driver') === 'redis') {
return new RedisPusherBroadcaster(
new Pusher($app['key'], $app['secret'], $app['id'], config('broadcasting.connections.websockets.options', [])),
$app['id'],
app('redis'),
config('broadcasting.connections.websockets.connection', null)
);
}
return new PusherBroadcaster(
new Pusher($app['key'], $app['secret'], $app['id'], config('broadcasting.connections.pusher.options', []))
);
}
}

View File

@ -3,12 +3,14 @@
namespace BeyondCode\LaravelWebSockets\Dashboard\Http\Controllers;
use BeyondCode\LaravelWebSockets\Apps\App;
use BeyondCode\LaravelWebSockets\Contracts\PushesToPusher;
use Illuminate\Broadcasting\Broadcasters\PusherBroadcaster;
use Illuminate\Http\Request;
use Pusher\Pusher;
class AuthenticateDashboard
{
use PushesToPusher;
/**
* Find the app by using the header
* and then reconstruct the PusherBroadcaster
@ -21,12 +23,11 @@ class AuthenticateDashboard
{
$app = App::findById($request->header('x-app-id'));
$broadcaster = new PusherBroadcaster(new Pusher(
$app->key,
$app->secret,
$app->id,
[]
));
$broadcaster = $this->getPusherBroadcaster([
'key' => $app->key,
'secret' => $app->secret,
'id' =>$app->id,
]);
/*
* Since the dashboard itself is already secured by the

View File

@ -2,13 +2,15 @@
namespace BeyondCode\LaravelWebSockets\Dashboard\Http\Controllers;
use BeyondCode\LaravelWebSockets\Contracts\PushesToPusher;
use BeyondCode\LaravelWebSockets\Statistics\Rules\AppId;
use Illuminate\Broadcasting\Broadcasters\PusherBroadcaster;
use Exception;
use Illuminate\Http\Request;
use Pusher\Pusher;
class SendMessage
{
use PushesToPusher;
/**
* Send the message to the requested channel.
*
@ -17,7 +19,7 @@ class SendMessage
*/
public function __invoke(Request $request)
{
$validated = $request->validate([
$request->validate([
'appId' => ['required', new AppId],
'key' => 'required|string',
'secret' => 'required|string',
@ -26,30 +28,27 @@ class SendMessage
'data' => 'required|json',
]);
$this->getPusherBroadcaster($validated)->broadcast(
[$validated['channel']],
$validated['event'],
json_decode($validated['data'], true)
);
$broadcaster = $this->getPusherBroadcaster([
'key' => $request->key,
'secret' => $request->secret,
'id' => $request->appId,
]);
return 'ok';
}
try {
$broadcaster->broadcast(
[$request->channel],
$request->event,
json_decode($request->data, true)
);
} catch (Exception $e) {
return response()->json([
'ok' => false,
'exception' => $e->getMessage(),
]);
}
/**
* Get the pusher broadcaster for the current request.
*
* @param array $validated
* @return \Illuminate\Broadcasting\Broadcasters\PusherBroadcaster
*/
protected function getPusherBroadcaster(array $validated): PusherBroadcaster
{
$pusher = new Pusher(
$validated['key'],
$validated['secret'],
$validated['appId'],
config('broadcasting.connections.pusher.options', [])
);
return new PusherBroadcaster($pusher);
return response()->json([
'ok' => true,
]);
}
}

View File

@ -5,7 +5,7 @@ namespace BeyondCode\LaravelWebSockets\Dashboard\Http\Controllers;
use BeyondCode\LaravelWebSockets\Statistics\Drivers\StatisticsDriver;
use Illuminate\Http\Request;
class DashboardApiController
class ShowStatistics
{
/**
* Get statistics for an app ID.
@ -15,7 +15,7 @@ class DashboardApiController
* @param mixed $appId
* @return \Illuminate\Http\Response
*/
public function getStatistics(Request $request, StatisticsDriver $driver, $appId)
public function __invoke(Request $request, StatisticsDriver $driver, $appId)
{
return $driver::get($appId, $request);
}

View File

@ -45,10 +45,10 @@ class RedisPusherBroadcaster extends Broadcaster
/**
* Create a new broadcaster instance.
*
* @param Pusher $pusher
* @param $appId
* @param \Illuminate\Contracts\Redis\Factory $redis
* @param string|null $connection
* @param Pusher $pusher
* @param mixed $appId
* @param \Illuminate\Contracts\Redis\Factory $redis
* @param string|null $connection
*/
public function __construct(Pusher $pusher, $appId, Redis $redis, $connection = null)
{
@ -63,7 +63,6 @@ class RedisPusherBroadcaster extends Broadcaster
*
* @param \Illuminate\Http\Request $request
* @return mixed
*
* @throws \Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException
*/
public function auth($request)
@ -83,8 +82,8 @@ class RedisPusherBroadcaster extends Broadcaster
/**
* Return the valid authentication response.
*
* @param \Illuminate\Http\Request $request
* @param mixed $result
* @param \Illuminate\Http\Request $request
* @param mixed $result
* @return mixed
* @throws \Pusher\PusherException
*/
@ -144,7 +143,7 @@ class RedisPusherBroadcaster extends Broadcaster
]);
foreach ($this->formatChannels($channels) as $channel) {
$connection->publish("{$this->appId}:$channel", $payload);
$connection->publish("{$this->appId}:{$channel}", $payload);
}
}
}

View File

@ -265,7 +265,7 @@ class RedisClient extends LocalClient
* @param string $payload
* @return void
*/
protected function onMessage(string $redisChannel, string $payload)
public function onMessage(string $redisChannel, string $payload)
{
$payload = json_decode($payload);

View File

@ -1,59 +0,0 @@
<?php
namespace BeyondCode\LaravelWebSockets\Statistics;
use React\Dns\Resolver\ResolverInterface;
use React\Promise\FulfilledPromise;
class DnsResolver implements ResolverInterface
{
/**
* The internal IP to use.
*
* @var string
*/
private $internalIp = '127.0.0.1';
/**
* Resolve the DNSes.
*
* @param string $domain
* @return \React\Promise\PromiseInterface
*/
public function resolve($domain)
{
return $this->resolveInternal($domain);
}
/**
* Resolve all domains.
*
* @param string $domain
* @param string $type
* @return FulfilledPromise
*/
public function resolveAll($domain, $type)
{
return $this->resolveInternal($domain, $type);
}
/**
* Resolve the internal domain.
*
* @param string $domain
* @param string $type
* @return FulfilledPromise
*/
private function resolveInternal($domain, $type = null)
{
return new FulfilledPromise($this->internalIp);
}
/**
* {@inheritdoc}
*/
public function __toString()
{
return $this->internalIp;
}
}

View File

@ -92,10 +92,10 @@ class DatabaseDriver implements StatisticsDriver
* Get the records to show to the dashboard.
*
* @param mixed $appId
* @param \Illuminate\Http\Request $request
* @param \Illuminate\Http\Request|null $request
* @return array
*/
public static function get($appId, Request $request): array
public static function get($appId, ?Request $request): array
{
$class = config('websockets.statistics.database.model');

View File

@ -61,10 +61,10 @@ interface StatisticsDriver
* Get the records to show to the dashboard.
*
* @param mixed $appId
* @param \Illuminate\Http\Request $request
* @param \Illuminate\Http\Request|null $request
* @return void
*/
public static function get($appId, Request $request);
public static function get($appId, ?Request $request);
/**
* Delete statistics from the store,

View File

@ -6,7 +6,6 @@ use BeyondCode\LaravelWebSockets\Apps\App;
use BeyondCode\LaravelWebSockets\Statistics\Drivers\StatisticsDriver;
use BeyondCode\LaravelWebSockets\Statistics\Statistic;
use BeyondCode\LaravelWebSockets\WebSockets\Channels\ChannelManager;
use Ratchet\ConnectionInterface;
class MemoryStatisticsLogger implements StatisticsLogger
{
@ -47,12 +46,12 @@ class MemoryStatisticsLogger implements StatisticsLogger
/**
* Handle the incoming websocket message.
*
* @param \Ratchet\ConnectionInterface $connection
* @param mixed $appId
* @return void
*/
public function webSocketMessage(ConnectionInterface $connection)
public function webSocketMessage($appId)
{
$this->findOrMakeStatisticForAppId($connection->app->id)
$this->findOrMakeStatisticForAppId($appId)
->webSocketMessage();
}
@ -71,24 +70,24 @@ class MemoryStatisticsLogger implements StatisticsLogger
/**
* Handle the new conection.
*
* @param \Ratchet\ConnectionInterface $connection
* @param mixed $appId
* @return void
*/
public function connection(ConnectionInterface $connection)
public function connection($appId)
{
$this->findOrMakeStatisticForAppId($connection->app->id)
$this->findOrMakeStatisticForAppId($appId)
->connection();
}
/**
* Handle disconnections.
*
* @param \Ratchet\ConnectionInterface $connection
* @param mixed $appId
* @return void
*/
public function disconnection(ConnectionInterface $connection)
public function disconnection($appId)
{
$this->findOrMakeStatisticForAppId($connection->app->id)
$this->findOrMakeStatisticForAppId($appId)
->disconnection();
}
@ -126,4 +125,14 @@ class MemoryStatisticsLogger implements StatisticsLogger
return $this->statistics[$appId];
}
/**
* Get the saved statistics.
*
* @return array
*/
public function getStatistics(): array
{
return $this->statistics;
}
}

View File

@ -4,7 +4,6 @@ namespace BeyondCode\LaravelWebSockets\Statistics\Logger;
use BeyondCode\LaravelWebSockets\Statistics\Drivers\StatisticsDriver;
use BeyondCode\LaravelWebSockets\WebSockets\Channels\ChannelManager;
use Ratchet\ConnectionInterface;
class NullStatisticsLogger implements StatisticsLogger
{
@ -38,10 +37,10 @@ class NullStatisticsLogger implements StatisticsLogger
/**
* Handle the incoming websocket message.
*
* @param \Ratchet\ConnectionInterface $connection
* @param mixed $appId
* @return void
*/
public function webSocketMessage(ConnectionInterface $connection)
public function webSocketMessage($appId)
{
//
}
@ -60,10 +59,10 @@ class NullStatisticsLogger implements StatisticsLogger
/**
* Handle the new conection.
*
* @param \Ratchet\ConnectionInterface $connection
* @param mixed $appId
* @return void
*/
public function connection(ConnectionInterface $connection)
public function connection($appId)
{
//
}
@ -71,10 +70,10 @@ class NullStatisticsLogger implements StatisticsLogger
/**
* Handle disconnections.
*
* @param \Ratchet\ConnectionInterface $connection
* @param mixed $appId
* @return void
*/
public function disconnection(ConnectionInterface $connection)
public function disconnection($appId)
{
//
}

View File

@ -2,17 +2,15 @@
namespace BeyondCode\LaravelWebSockets\Statistics\Logger;
use Ratchet\ConnectionInterface;
interface StatisticsLogger
{
/**
* Handle the incoming websocket message.
*
* @param \Ratchet\ConnectionInterface $connection
* @param mixed $appId
* @return void
*/
public function webSocketMessage(ConnectionInterface $connection);
public function webSocketMessage($appId);
/**
* Handle the incoming API message.
@ -25,18 +23,18 @@ interface StatisticsLogger
/**
* Handle the new conection.
*
* @param \Ratchet\ConnectionInterface $connection
* @param mixed $appId
* @return void
*/
public function connection(ConnectionInterface $connection);
public function connection($appId);
/**
* Handle disconnections.
*
* @param \Ratchet\ConnectionInterface $connection
* @param mixed $appId
* @return void
*/
public function disconnection(ConnectionInterface $connection);
public function disconnection($appId);
/**
* Save all the stored statistics.

View File

@ -65,7 +65,7 @@ class WebSocketHandler implements MessageComponentInterface
$message->respond();
StatisticsLogger::webSocketMessage($connection);
StatisticsLogger::webSocketMessage($connection->app->id);
}
/**
@ -82,7 +82,7 @@ class WebSocketHandler implements MessageComponentInterface
'socketId' => $connection->socketId,
]);
StatisticsLogger::disconnection($connection);
StatisticsLogger::disconnection($connection->app->id);
}
/**
@ -200,7 +200,7 @@ class WebSocketHandler implements MessageComponentInterface
'socketId' => $connection->socketId,
]);
StatisticsLogger::connection($connection);
StatisticsLogger::connection($connection->app->id);
return $this;
}

View File

@ -4,9 +4,9 @@ namespace BeyondCode\LaravelWebSockets;
use BeyondCode\LaravelWebSockets\Apps\AppManager;
use BeyondCode\LaravelWebSockets\Dashboard\Http\Controllers\AuthenticateDashboard;
use BeyondCode\LaravelWebSockets\Dashboard\Http\Controllers\DashboardApiController;
use BeyondCode\LaravelWebSockets\Dashboard\Http\Controllers\SendMessage;
use BeyondCode\LaravelWebSockets\Dashboard\Http\Controllers\ShowDashboard;
use BeyondCode\LaravelWebSockets\Dashboard\Http\Controllers\ShowStatistics;
use BeyondCode\LaravelWebSockets\Dashboard\Http\Middleware\Authorize as AuthorizeDashboard;
use BeyondCode\LaravelWebSockets\PubSub\Broadcasters\RedisPusherBroadcaster;
use BeyondCode\LaravelWebSockets\Server\Router;
@ -118,13 +118,15 @@ class WebSocketsServiceProvider extends ServiceProvider
*/
protected function registerDashboardRoutes()
{
Route::prefix(config('websockets.dashboard.path'))->group(function () {
Route::middleware(config('websockets.dashboard.middleware', [AuthorizeDashboard::class]))->group(function () {
Route::get('/', ShowDashboard::class);
Route::get('/api/{appId}/statistics', [DashboardApiController::class, 'getStatistics']);
Route::post('auth', AuthenticateDashboard::class);
Route::post('event', SendMessage::class);
});
Route::group([
'prefix' => config('websockets.dashboard.path'),
'as' => 'laravel-websockets.',
'middleware' => config('websockets.dashboard.middleware', [AuthorizeDashboard::class]),
], function () {
Route::get('/', ShowDashboard::class)->name('dashboard');
Route::get('/api/{appId}/statistics', ShowStatistics::class)->name('statistics');
Route::post('/auth', AuthenticateDashboard::class)->name('auth');
Route::post('/event', SendMessage::class)->name('event');
});
return $this;

View File

@ -22,12 +22,12 @@ class ChannelReplicationTest extends TestCase
{
$connection = $this->getWebSocketConnection();
$message = new Message(json_encode([
$message = new Message([
'event' => 'pusher:subscribe',
'data' => [
'channel' => 'basic-channel',
],
]));
]);
$this->pusherServer->onOpen($connection);
@ -47,12 +47,12 @@ class ChannelReplicationTest extends TestCase
$this->assertTrue($channel->hasConnections());
$message = new Message(json_encode([
$message = new Message([
'event' => 'pusher:unsubscribe',
'data' => [
'channel' => 'test-channel',
],
]));
]);
$this->pusherServer->onMessage($connection, $message);
@ -67,7 +67,7 @@ class ChannelReplicationTest extends TestCase
$connection = $this->getConnectedWebSocketConnection(['test-channel']);
$message = new Message('{"event": "client-test", "data": {}, "channel": "test-channel"}');
$message = new Message(['event' => 'client-test', 'data' => [], 'channel' => 'test-channel']);
$this->pusherServer->onMessage($connection, $message);
@ -84,7 +84,7 @@ class ChannelReplicationTest extends TestCase
$connection = $this->getConnectedWebSocketConnection(['test-channel']);
$message = new Message('{"event": "client-test", "data": {}, "channel": "test-channel"}');
$message = new Message(['event' => 'client-test', 'data' => [], 'channel' => 'test-channel']);
$this->pusherServer->onMessage($connection, $message);
@ -147,9 +147,9 @@ class ChannelReplicationTest extends TestCase
{
$connection = $this->getConnectedWebSocketConnection();
$message = new Message(json_encode([
$message = new Message([
'event' => 'pusher:ping',
]));
]);
$this->pusherServer->onMessage($connection, $message);

View File

@ -12,12 +12,12 @@ class ChannelTest extends TestCase
{
$connection = $this->getWebSocketConnection();
$message = new Message(json_encode([
$message = new Message([
'event' => 'pusher:subscribe',
'data' => [
'channel' => 'basic-channel',
],
]));
]);
$this->pusherServer->onOpen($connection);
@ -37,12 +37,12 @@ class ChannelTest extends TestCase
$this->assertTrue($channel->hasConnections());
$message = new Message(json_encode([
$message = new Message([
'event' => 'pusher:unsubscribe',
'data' => [
'channel' => 'test-channel',
],
]));
]);
$this->pusherServer->onMessage($connection, $message);
@ -57,7 +57,7 @@ class ChannelTest extends TestCase
$connection = $this->getConnectedWebSocketConnection(['test-channel']);
$message = new Message('{"event": "client-test", "data": {}, "channel": "test-channel"}');
$message = new Message(['event' => 'client-test', 'data' => [], 'channel' => 'test-channel']);
$this->pusherServer->onMessage($connection, $message);
@ -74,7 +74,7 @@ class ChannelTest extends TestCase
$connection = $this->getConnectedWebSocketConnection(['test-channel']);
$message = new Message('{"event": "client-test", "data": {}, "channel": "test-channel"}');
$message = new Message(['event' => 'client-test', 'data' => [], 'channel' => 'test-channel']);
$this->pusherServer->onMessage($connection, $message);
@ -137,9 +137,9 @@ class ChannelTest extends TestCase
{
$connection = $this->getConnectedWebSocketConnection();
$message = new Message(json_encode([
$message = new Message([
'event' => 'pusher:ping',
]));
]);
$this->pusherServer->onMessage($connection, $message);

View File

@ -33,14 +33,14 @@ class PresenceChannelReplicationTest extends TestCase
$signature = "{$connection->socketId}:presence-channel:".json_encode($channelData);
$message = new Message(json_encode([
$message = new Message([
'event' => 'pusher:subscribe',
'data' => [
'auth' => $connection->app->key.':'.hash_hmac('sha256', $signature, $connection->app->secret),
'channel' => 'presence-channel',
'channel_data' => json_encode($channelData),
],
]));
]);
$this->pusherServer->onMessage($connection, $message);
@ -67,14 +67,14 @@ class PresenceChannelReplicationTest extends TestCase
$signature = "{$connection->socketId}:presence-channel:".json_encode($channelData);
$message = new Message(json_encode([
$message = new Message([
'event' => 'pusher:subscribe',
'data' => [
'auth' => $connection->app->key.':'.hash_hmac('sha256', $signature, $connection->app->secret),
'channel' => 'presence-channel',
'channel_data' => json_encode($channelData),
],
]));
]);
$this->pusherServer->onMessage($connection, $message);
@ -89,13 +89,13 @@ class PresenceChannelReplicationTest extends TestCase
$this->getPublishClient()
->resetAssertions();
$message = new Message(json_encode([
$message = new Message([
'event' => 'pusher:unsubscribe',
'data' => [
'auth' => $connection->app->key.':'.hash_hmac('sha256', $signature, $connection->app->secret),
'channel' => 'presence-channel',
],
]));
]);
$this->pusherServer->onMessage($connection, $message);
@ -117,14 +117,14 @@ class PresenceChannelReplicationTest extends TestCase
$signature = "{$connection->socketId}:presence-channel:".json_encode($channelData);
$message = new Message(json_encode([
$message = new Message([
'event' => 'pusher:subscribe',
'data' => [
'auth' => $connection->app->key.':'.hash_hmac('sha256', $signature, $connection->app->secret),
'channel' => 'presence-channel',
'channel_data' => json_encode($channelData),
],
]));
]);
$this->pusherServer->onMessage($connection, $message);

View File

@ -15,13 +15,13 @@ class PresenceChannelTest extends TestCase
$connection = $this->getWebSocketConnection();
$message = new Message(json_encode([
$message = new Message([
'event' => 'pusher:subscribe',
'data' => [
'auth' => 'invalid',
'channel' => 'presence-channel',
],
]));
]);
$this->pusherServer->onOpen($connection);
@ -46,14 +46,14 @@ class PresenceChannelTest extends TestCase
$signature = "{$connection->socketId}:presence-channel:".json_encode($channelData);
$message = new Message(json_encode([
$message = new Message([
'event' => 'pusher:subscribe',
'data' => [
'auth' => $connection->app->key.':'.hash_hmac('sha256', $signature, $connection->app->secret),
'channel' => 'presence-channel',
'channel_data' => json_encode($channelData),
],
]));
]);
$this->pusherServer->onMessage($connection, $message);
@ -77,14 +77,14 @@ class PresenceChannelTest extends TestCase
$signature = "{$connection->socketId}:presence-channel:".json_encode($channelData);
$message = new Message(json_encode([
$message = new Message([
'event' => 'pusher:subscribe',
'data' => [
'auth' => $connection->app->key.':'.hash_hmac('sha256', $signature, $connection->app->secret),
'channel' => 'presence-channel',
'channel_data' => json_encode($channelData),
],
]));
]);
$this->pusherServer->onMessage($connection, $message);
@ -92,13 +92,13 @@ class PresenceChannelTest extends TestCase
'channel' => 'presence-channel',
]);
$message = new Message(json_encode([
$message = new Message([
'event' => 'pusher:unsubscribe',
'data' => [
'auth' => $connection->app->key.':'.hash_hmac('sha256', $signature, $connection->app->secret),
'channel' => 'presence-channel',
],
]));
]);
$this->pusherServer->onMessage($connection, $message);
}
@ -118,14 +118,14 @@ class PresenceChannelTest extends TestCase
$signature = "{$connection->socketId}:presence-channel:".json_encode($channelData);
$message = new Message(json_encode([
$message = new Message([
'event' => 'pusher:subscribe',
'data' => [
'auth' => $connection->app->key.':'.hash_hmac('sha256', $signature, $connection->app->secret),
'channel' => 'presence-channel',
'channel_data' => json_encode($channelData),
],
]));
]);
$this->pusherServer->onMessage($connection, $message);
@ -150,13 +150,13 @@ class PresenceChannelTest extends TestCase
$signature = "{$connection->socketId}:presence-channel:".json_encode($channelData);
$message = new Message(json_encode([
$message = new Message([
'event' => 'pusher:unsubscribe',
'data' => [
'auth' => $connection->app->key.':'.hash_hmac('sha256', $signature, $connection->app->secret),
'channel' => 'presence-channel',
],
]));
]);
$this->pusherServer->onMessage($connection, $message);

View File

@ -25,13 +25,13 @@ class PrivateChannelReplicationTest extends TestCase
$connection = $this->getWebSocketConnection();
$message = new Message(json_encode([
$message = new Message([
'event' => 'pusher:subscribe',
'data' => [
'auth' => 'invalid',
'channel' => 'private-channel',
],
]));
]);
$this->pusherServer->onOpen($connection);
@ -49,13 +49,13 @@ class PrivateChannelReplicationTest extends TestCase
$hashedAppSecret = hash_hmac('sha256', $signature, $connection->app->secret);
$message = new Message(json_encode([
$message = new Message([
'event' => 'pusher:subscribe',
'data' => [
'auth' => "{$connection->app->key}:{$hashedAppSecret}",
'channel' => 'private-channel',
],
]));
]);
$this->pusherServer->onMessage($connection, $message);

View File

@ -15,13 +15,13 @@ class PrivateChannelTest extends TestCase
$connection = $this->getWebSocketConnection();
$message = new Message(json_encode([
$message = new Message([
'event' => 'pusher:subscribe',
'data' => [
'auth' => 'invalid',
'channel' => 'private-channel',
],
]));
]);
$this->pusherServer->onOpen($connection);
@ -39,13 +39,13 @@ class PrivateChannelTest extends TestCase
$hashedAppSecret = hash_hmac('sha256', $signature, $connection->app->secret);
$message = new Message(json_encode([
$message = new Message([
'event' => 'pusher:subscribe',
'data' => [
'auth' => "{$connection->app->key}:{$hashedAppSecret}",
'channel' => 'private-channel',
],
]));
]);
$this->pusherServer->onMessage($connection, $message);

View File

@ -42,4 +42,34 @@ class CleanStatisticsTest extends TestCase
$this->assertCount(0, WebSocketsStatisticsEntry::where('created_at', '<', $cutOffDate)->get());
}
/** @test */
public function it_can_clean_the_statistics_for_app_id_only()
{
Collection::times(60)->each(function (int $index) {
WebSocketsStatisticsEntry::create([
'app_id' => 'app_id',
'peak_connection_count' => 1,
'websocket_message_count' => 2,
'api_message_count' => 3,
'created_at' => Carbon::now()->subDays($index)->startOfDay(),
]);
});
Collection::times(60)->each(function (int $index) {
WebSocketsStatisticsEntry::create([
'app_id' => 'app_id2',
'peak_connection_count' => 1,
'websocket_message_count' => 2,
'api_message_count' => 3,
'created_at' => Carbon::now()->subDays($index)->startOfDay(),
]);
});
$this->assertCount(120, WebSocketsStatisticsEntry::all());
Artisan::call('websockets:clean', ['appId' => 'app_id']);
$this->assertCount(91, WebSocketsStatisticsEntry::all());
}
}

View File

@ -9,7 +9,7 @@ class StartWebSocketServerTest extends TestCase
/** @test */
public function does_not_fail_if_building_up()
{
$this->artisan('websockets:serve', ['--test' => true]);
$this->artisan('websockets:serve', ['--test' => true, '--debug' => true]);
$this->assertTrue(true);
}

View File

@ -58,7 +58,7 @@ class ConnectionTest extends TestCase
{
$connection = $this->getWebSocketConnection();
$message = new Message('{"event": "pusher:ping"}');
$message = new Message(['event' => 'pusher:ping']);
$this->pusherServer->onOpen($connection);

View File

@ -0,0 +1,100 @@
<?php
namespace BeyondCode\LaravelWebSockets\Tests\Dashboard;
use BeyondCode\LaravelWebSockets\Tests\Mocks\Message;
use BeyondCode\LaravelWebSockets\Tests\Models\User;
use BeyondCode\LaravelWebSockets\Tests\TestCase;
class AuthTest extends TestCase
{
/** @test */
public function can_authenticate_dashboard_over_channel()
{
$connection = $this->getConnectedWebSocketConnection(['test-channel']);
$this->pusherServer->onOpen($connection);
$this->actingAs(factory(User::class)->create())
->json('POST', route('laravel-websockets.auth'), [
'socket_id' => $connection->socketId,
'channel_name' => 'test-channel',
], ['x-app-id' => '1234'])
->seeJsonStructure([
'auth',
'channel_data',
]);
}
/** @test */
public function can_authenticate_dashboard_over_private_channel()
{
$connection = $this->getWebSocketConnection();
$this->pusherServer->onOpen($connection);
$signature = "{$connection->socketId}:private-channel";
$hashedAppSecret = hash_hmac('sha256', $signature, $connection->app->secret);
$message = new Message([
'event' => 'pusher:subscribe',
'data' => [
'auth' => "{$connection->app->key}:{$hashedAppSecret}",
'channel' => 'private-channel',
],
]);
$this->pusherServer->onMessage($connection, $message);
$connection->assertSentEvent('pusher_internal:subscription_succeeded', [
'channel' => 'private-channel',
]);
$this->actingAs(factory(User::class)->create())
->json('POST', route('laravel-websockets.auth'), [
'socket_id' => $connection->socketId,
'channel_name' => 'private-test-channel',
], ['x-app-id' => '1234'])
->seeJsonStructure([
'auth',
]);
}
/** @test */
public function can_authenticate_dashboard_over_presence_channel()
{
$connection = $this->getWebSocketConnection();
$this->pusherServer->onOpen($connection);
$channelData = [
'user_id' => 1,
'user_info' => [
'name' => 'Marcel',
],
];
$signature = "{$connection->socketId}:presence-channel:".json_encode($channelData);
$message = new Message([
'event' => 'pusher:subscribe',
'data' => [
'auth' => $connection->app->key.':'.hash_hmac('sha256', $signature, $connection->app->secret),
'channel' => 'presence-channel',
'channel_data' => json_encode($channelData),
],
]);
$this->pusherServer->onMessage($connection, $message);
$this->actingAs(factory(User::class)->create())
->json('POST', route('laravel-websockets.auth'), [
'socket_id' => $connection->socketId,
'channel_name' => 'presence-channel',
], ['x-app-id' => '1234'])
->seeJsonStructure([
'auth',
]);
}
}

View File

@ -0,0 +1,25 @@
<?php
namespace BeyondCode\LaravelWebSockets\Tests\Dashboard;
use BeyondCode\LaravelWebSockets\Tests\Models\User;
use BeyondCode\LaravelWebSockets\Tests\TestCase;
class DashboardTest extends TestCase
{
/** @test */
public function cant_see_dashboard_without_authorization()
{
$this->get(route('laravel-websockets.dashboard'))
->assertResponseStatus(403);
}
/** @test */
public function can_see_dashboard()
{
$this->actingAs(factory(User::class)->create())
->get(route('laravel-websockets.dashboard'))
->assertResponseOk()
->see('WebSockets Dashboard');
}
}

View File

@ -0,0 +1,76 @@
<?php
namespace BeyondCode\LaravelWebSockets\Tests\Dashboard;
use BeyondCode\LaravelWebSockets\Tests\Models\User;
use BeyondCode\LaravelWebSockets\Tests\TestCase;
class SendMessageTest extends TestCase
{
/** @test */
public function can_send_message()
{
$this->skipOnRedisReplication();
// Because the Pusher server is not active,
// we expect it to turn out ok: false.
$this->actingAs(factory(User::class)->create())
->json('POST', route('laravel-websockets.event'), [
'appId' => '1234',
'key' => 'TestKey',
'secret' => 'TestSecret',
'channel' => 'test-channel',
'event' => 'some-event',
'data' => json_encode(['data' => 'yes']),
])
->seeJson([
'ok' => false,
]);
}
/** @test */
public function can_send_message_on_redis_replication()
{
$this->skipOnLocalReplication();
// Because the Pusher server is not active,
// we expect it to turn out ok: false.
// However, the driver is set to redis,
// so Redis would take care of this
// and stream the message to all active servers instead.
$this->actingAs(factory(User::class)->create())
->json('POST', route('laravel-websockets.event'), [
'appId' => '1234',
'key' => 'TestKey',
'secret' => 'TestSecret',
'channel' => 'test-channel',
'event' => 'some-event',
'data' => json_encode(['data' => 'yes']),
])
->seeJson([
'ok' => true,
]);
}
/** @test */
public function cant_send_message_for_invalid_app()
{
$this->skipOnRedisReplication();
// Because the Pusher server is not active,
// we expect it to turn out ok: false.
$this->actingAs(factory(User::class)->create())
->json('POST', route('laravel-websockets.event'), [
'appId' => '9999',
'key' => 'TestKey',
'secret' => 'TestSecret',
'channel' => 'test-channel',
'event' => 'some-event',
'data' => json_encode(['data' => 'yes']),
])
->assertResponseStatus(422);
}
}

View File

@ -0,0 +1,63 @@
<?php
namespace BeyondCode\LaravelWebSockets\Tests\Dashboard;
use BeyondCode\LaravelWebSockets\Statistics\Logger\MemoryStatisticsLogger;
use BeyondCode\LaravelWebSockets\Tests\Models\User;
use BeyondCode\LaravelWebSockets\Tests\TestCase;
class StatisticsTest extends TestCase
{
/** @test */
public function can_get_statistics()
{
$connection = $this->getConnectedWebSocketConnection(['channel-1']);
$logger = new MemoryStatisticsLogger(
$this->channelManager,
$this->statisticsDriver
);
$logger->webSocketMessage($connection->app->id);
$logger->apiMessage($connection->app->id);
$logger->connection($connection->app->id);
$logger->disconnection($connection->app->id);
$logger->save();
$this->actingAs(factory(User::class)->create())
->json('GET', route('laravel-websockets.statistics', ['appId' => '1234']))
->assertResponseOk()
->seeJsonStructure([
'peak_connections' => ['x', 'y'],
'websocket_message_count' => ['x', 'y'],
'api_message_count' => ['x', 'y'],
]);
}
/** @test */
public function cant_get_statistics_for_invalid_app_id()
{
$connection = $this->getConnectedWebSocketConnection(['channel-1']);
$logger = new MemoryStatisticsLogger(
$this->channelManager,
$this->statisticsDriver
);
$logger->webSocketMessage($connection->app->id);
$logger->apiMessage($connection->app->id);
$logger->connection($connection->app->id);
$logger->disconnection($connection->app->id);
$logger->save();
$this->actingAs(factory(User::class)->create())
->json('GET', route('laravel-websockets.statistics', ['appId' => 'not_found']))
->seeJson([
'peak_connections' => ['x' => [], 'y' => []],
'websocket_message_count' => ['x' => [], 'y' => []],
'api_message_count' => ['x' => [], 'y' => []],
]);
}
}

View File

@ -12,13 +12,13 @@ class PusherClientMessageTest extends TestCase
{
$connection = $this->getConnectedWebSocketConnection(['test-channel']);
$message = new Message(json_encode([
$message = new Message([
'event' => 'client-test',
'channel' => 'test-channel',
'data' => [
'client-event' => 'test',
],
]));
]);
$this->pusherServer->onMessage($connection, $message);
@ -42,13 +42,13 @@ class PusherClientMessageTest extends TestCase
$connection1 = $this->getConnectedWebSocketConnection(['test-channel']);
$connection2 = $this->getConnectedWebSocketConnection(['test-channel']);
$message = new Message(json_encode([
$message = new Message([
'event' => 'client-test',
'channel' => 'test-channel',
'data' => [
'client-event' => 'test',
],
]));
]);
$this->pusherServer->onMessage($connection1, $message);

View File

@ -2,17 +2,35 @@
namespace BeyondCode\LaravelWebSockets\Tests\Mocks;
class Message extends \Ratchet\RFC6455\Messaging\Message
use Ratchet\RFC6455\Messaging\Message as BaseMessage;
class Message extends BaseMessage
{
/**
* The payload as array.
*
* @var array
*/
protected $payload;
public function __construct($payload)
/**
* Create a new message instance.
*
* @param array $payload
* @return void
*/
public function __construct(array $payload)
{
$this->payload = $payload;
}
public function getPayload()
/**
* Get the payload as json-encoded string.
*
* @return string
*/
public function getPayload(): string
{
return $this->payload;
return json_encode($this->payload);
}
}

16
tests/Models/User.php Normal file
View File

@ -0,0 +1,16 @@
<?php
namespace BeyondCode\LaravelWebSockets\Tests\Models;
use Illuminate\Foundation\Auth\User as Authenticatable;
class User extends Authenticatable
{
protected $fillable = [
'name', 'email', 'password',
];
protected $hidden = [
'password', 'remember_token',
];
}

View File

@ -2,7 +2,10 @@
namespace BeyondCode\LaravelWebSockets\Tests\PubSub;
use BeyondCode\LaravelWebSockets\PubSub\Drivers\RedisClient;
use BeyondCode\LaravelWebSockets\Tests\Mocks\RedisFactory;
use BeyondCode\LaravelWebSockets\Tests\TestCase;
use React\EventLoop\Factory as LoopFactory;
class RedisDriverTest extends TestCase
{
@ -46,4 +49,35 @@ class RedisDriverTest extends TestCase
'1234:test-channel', $payload,
]);
}
/** @test */
public function redis_listener_responds_properly_on_payload_by_direct_call()
{
$connection = $this->getConnectedWebSocketConnection(['test-channel']);
$this->pusherServer->onOpen($connection);
$channelData = [
'user_id' => 1,
'user_info' => [
'name' => 'Marcel',
],
];
$payload = json_encode([
'appId' => '1234',
'event' => 'test',
'data' => $channelData,
'socket' => $connection->socketId,
]);
$client = (new RedisClient)->boot(
LoopFactory::create(), RedisFactory::class
);
$client->onMessage('1234:test-channel', $payload);
$client->getSubscribeClient()
->assertEventDispatched('message');
}
}

View File

@ -3,6 +3,9 @@
namespace BeyondCode\LaravelWebSockets\Tests\Statistics\Controllers;
use BeyondCode\LaravelWebSockets\Facades\StatisticsLogger;
use BeyondCode\LaravelWebSockets\Statistics\Logger\MemoryStatisticsLogger;
use BeyondCode\LaravelWebSockets\Statistics\Logger\NullStatisticsLogger;
use BeyondCode\LaravelWebSockets\Statistics\Models\WebSocketsStatisticsEntry;
use BeyondCode\LaravelWebSockets\Tests\TestCase;
class StatisticsLoggerTest extends TestCase
@ -43,4 +46,50 @@ class StatisticsLoggerTest extends TestCase
$this->assertEquals(1, StatisticsLogger::getForAppId(1234)['peak_connection_count']);
}
/** @test */
public function it_counts_connections_with_memory_logger()
{
$connection = $this->getConnectedWebSocketConnection(['channel-1']);
$logger = new MemoryStatisticsLogger(
$this->channelManager,
$this->statisticsDriver
);
$logger->webSocketMessage($connection->app->id);
$logger->apiMessage($connection->app->id);
$logger->connection($connection->app->id);
$logger->disconnection($connection->app->id);
$logger->save();
$this->assertCount(1, WebSocketsStatisticsEntry::all());
$entry = WebSocketsStatisticsEntry::first();
$this->assertEquals(1, $entry->peak_connection_count);
$this->assertEquals(1, $entry->websocket_message_count);
$this->assertEquals(1, $entry->api_message_count);
}
/** @test */
public function it_counts_connections_with_null_logger()
{
$connection = $this->getConnectedWebSocketConnection(['channel-1']);
$logger = new NullStatisticsLogger(
$this->channelManager,
$this->statisticsDriver
);
$logger->webSocketMessage($connection->app->id);
$logger->apiMessage($connection->app->id);
$logger->connection($connection->app->id);
$logger->disconnection($connection->app->id);
$logger->save();
$this->assertCount(0, WebSocketsStatisticsEntry::all());
}
}

View File

@ -12,10 +12,11 @@ use BeyondCode\LaravelWebSockets\Tests\Mocks\Message;
use BeyondCode\LaravelWebSockets\Tests\Statistics\Logger\FakeStatisticsLogger;
use BeyondCode\LaravelWebSockets\WebSockets\Channels\ChannelManager;
use GuzzleHttp\Psr7\Request;
use Orchestra\Testbench\BrowserKit\TestCase as BaseTestCase;
use Ratchet\ConnectionInterface;
use React\EventLoop\Factory as LoopFactory;
abstract class TestCase extends \Orchestra\Testbench\TestCase
abstract class TestCase extends BaseTestCase
{
/**
* A test Pusher server.
@ -31,6 +32,13 @@ abstract class TestCase extends \Orchestra\Testbench\TestCase
*/
protected $channelManager;
/**
* The used statistics driver.
*
* @var \BeyondCode\LaravelWebSockets\Statistics\Drivers\StatisticsDriver
*/
protected $statisticsDriver;
/**
* {@inheritdoc}
*/
@ -38,9 +46,17 @@ abstract class TestCase extends \Orchestra\Testbench\TestCase
{
parent::setUp();
$this->pusherServer = app(config('websockets.handlers.websocket'));
$this->resetDatabase();
$this->channelManager = app(ChannelManager::class);
$this->loadLaravelMigrations(['--database' => 'sqlite']);
$this->withFactories(__DIR__.'/database/factories');
$this->pusherServer = $this->app->make(config('websockets.handlers.websocket'));
$this->channelManager = $this->app->make(ChannelManager::class);
$this->statisticsDriver = $this->app->make(StatisticsDriver::class);
StatisticsLogger::swap(new FakeStatisticsLogger(
$this->channelManager,
@ -59,6 +75,7 @@ abstract class TestCase extends \Orchestra\Testbench\TestCase
{
return [
\BeyondCode\LaravelWebSockets\WebSocketsServiceProvider::class,
TestServiceProvider::class,
];
}
@ -67,6 +84,18 @@ abstract class TestCase extends \Orchestra\Testbench\TestCase
*/
protected function getEnvironmentSetUp($app)
{
$app['config']->set('app.key', 'wslxrEFGWY6GfGhvN9L3wH3KSRJQQpBD');
$app['config']->set('auth.providers.users.model', Models\User::class);
$app['config']->set('database.default', 'sqlite');
$app['config']->set('database.connections.sqlite', [
'driver' => 'sqlite',
'database' => __DIR__.'/database.sqlite',
'prefix' => '',
]);
$app['config']->set('websockets.apps', [
[
'name' => 'Test App',
@ -160,12 +189,12 @@ abstract class TestCase extends \Orchestra\Testbench\TestCase
$this->pusherServer->onOpen($connection);
foreach ($channelsToJoin as $channel) {
$message = new Message(json_encode([
$message = new Message([
'event' => 'pusher:subscribe',
'data' => [
'channel' => $channel,
],
]));
]);
$this->pusherServer->onMessage($connection, $message);
}
@ -194,14 +223,14 @@ abstract class TestCase extends \Orchestra\Testbench\TestCase
$signature = "{$connection->socketId}:{$channel}:".json_encode($channelData);
$message = new Message(json_encode([
$message = new Message([
'event' => 'pusher:subscribe',
'data' => [
'auth' => $connection->app->key.':'.hash_hmac('sha256', $signature, $connection->app->secret),
'channel' => $channel,
'channel_data' => json_encode($channelData),
],
]));
]);
$this->pusherServer->onMessage($connection, $message);
@ -295,4 +324,14 @@ abstract class TestCase extends \Orchestra\Testbench\TestCase
->make(ReplicationInterface::class)
->getPublishClient();
}
/**
* Reset the database.
*
* @return void
*/
protected function resetDatabase()
{
file_put_contents(__DIR__.'/database.sqlite', null);
}
}

View File

@ -0,0 +1,31 @@
<?php
namespace BeyondCode\LaravelWebSockets\Tests;
use Illuminate\Support\Facades\Gate;
use Illuminate\Support\ServiceProvider;
class TestServiceProvider extends ServiceProvider
{
/**
* Boot the service provider.
*
* @return void
*/
public function boot()
{
Gate::define('viewWebSocketsDashboard', function ($user = null) {
return ! is_null($user);
});
}
/**
* Register the service provider.
*
* @return void
*/
public function register()
{
//
}
}

View File

@ -0,0 +1,22 @@
<?php
/*
|--------------------------------------------------------------------------
| Model Factories
|--------------------------------------------------------------------------
|
| This directory should contain each of the model factory definitions for
| your application. Factories provide a convenient way to generate new
| model instances for testing / seeding your application's database.
|
*/
use Illuminate\Support\Str;
$factory->define(\BeyondCode\LaravelWebSockets\Tests\Models\User::class, function () {
return [
'name' => 'Name'.Str::random(5),
'email' => Str::random(5).'@gmail.com',
'password' => '$2y$10$TKh8H1.PfQx37YgCzwiKb.KjNyWgaHb9cbcoQgdIVFlYg7B77UdFm', // secret
'remember_token' => Str::random(10),
];
});