Skip to content

Commit

Permalink
Rewrite tests for PresenceChannelUsersRedisRepository
Browse files Browse the repository at this point in the history
  • Loading branch information
slavarazum committed Dec 8, 2023
1 parent 02356aa commit 735c972
Show file tree
Hide file tree
Showing 4 changed files with 82 additions and 136 deletions.
4 changes: 2 additions & 2 deletions src/Storage/PresenceChannelUsersRedisRepository.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,12 @@ public function __construct()

protected function channelMemberKey(string $channel, string ...$suffixes): string
{
return implode(':', \array_merge([$this->prefix.'channels', $channel], $suffixes));
return implode(':', \array_merge(['broadcasting_channels', $channel], $suffixes));
}

protected function userChannelsKey(Authenticatable $user): string
{
return implode(':', [$this->prefix.'channels', $this->userKey($user), 'user_channels']);
return implode(':', ['broadcasting_channels', $this->userKey($user), 'user_channels']);
}

protected function serialize(array $value): string
Expand Down
20 changes: 20 additions & 0 deletions tests/InteractsWithRedis.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?php

namespace Qruto\LaravelWave\Tests;

trait InteractsWithRedis
{
use \Illuminate\Foundation\Testing\Concerns\InteractsWithRedis;

public static function redisDriverProvider()
{
return [
['phpredis'],
];
}

public function connection()
{
return $this->redis['phpredis']->connection();
}
}
7 changes: 0 additions & 7 deletions tests/Pest.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,6 @@
uses(TestCase::class)->in(__DIR__);

uses()->beforeEach(function () {
$redisMock = new RedisConnectionMock();

$this->instance('redis', $redisMock);

$redisMock->flushdb();
$redisMock->flushEventsQueue();

$this->user = User::factory()->create();

Broadcast::channel('private-channel', fn () => true);
Expand Down
187 changes: 60 additions & 127 deletions tests/PresenceChannelUsersRedisRepositoryTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,194 +3,127 @@
use Illuminate\Contracts\Auth\Authenticatable;
use Illuminate\Support\Facades\Redis;
use Qruto\LaravelWave\Storage\PresenceChannelUsersRedisRepository;
use Qruto\LaravelWave\Tests\InteractsWithRedis;
use Qruto\LaravelWave\Tests\Support\User;

uses(InteractsWithRedis::class);

beforeEach(function () {
Redis::partialMock()->shouldReceive('connection')->once()->andReturnSelf();
$this->setUpRedis();
Redis::partialMock()->shouldReceive('connection')->once()->andReturn($this->connection());
$this->repository = new PresenceChannelUsersRedisRepository();
});

afterEach(function () {
$this->tearDownRedis();
});

$connectionId = 'random-connection-id';
$channel = 'community';
$channel = 'presence-community';

function channelMemberKey(string $channel, string ...$suffixes): string
{
return implode(':', \array_merge(["laravel_database_channels:$channel"], $suffixes));
return implode(':', array_merge(["broadcasting_channels:$channel"], $suffixes));
}

function userChannelsKey(Authenticatable $user): string
{
return implode(':', ['laravel_database_channels', $user->getAuthIdentifier(), 'user_channels']);
return implode(':', ['broadcasting_channels', $user->getAuthIdentifier(), 'user_channels']);
}

it('can add a new user to a presence channel', function () use ($connectionId, $channel) {
Redis::shouldReceive('sadd')->once()->withArgs([
'laravel_database_channels:community:1:user_sockets',
$connectionId,
])->andReturn(null);

Redis::shouldReceive('hset')->once()->withArgs([
'laravel_database_channels:community:users',
$this->user->getAuthIdentifierForBroadcasting(),
json_encode(['email' => $this->user->email]),
])->andReturn(null);

Redis::shouldReceive('sadd')->once()->withArgs([
userChannelsKey($this->user),
$channel,
])->andReturn(null);
$userKey = $this->user->getAuthIdentifierForBroadcasting();
$usersHashKey = channelMemberKey($channel, 'users');
$socketsSetKey = channelMemberKey($channel, $userKey, 'user_sockets');
$userChannelsKey = userChannelsKey($this->user);

expect($this->repository->join(
$channel,
$this->user,
['email' => $this->user->email],
$connectionId
))->toBe(true);
))->toBeTrue()
->and((bool) $this->connection()->exists($socketsSetKey))->toBeTrue()
->and((bool) $this->connection()->exists($userChannelsKey))->toBeTrue()
->and((bool) $this->connection()->exists($usersHashKey))->toBeTrue()
->and((bool) $this->connection()->sismember($socketsSetKey, $connectionId))->toBeTrue()
->and((bool) $this->connection()->hexists($usersHashKey, $userKey))->toBeTrue()
->and($this->connection()->hget($usersHashKey, $userKey))->toBe(json_encode(['email' => $this->user->email]))
->and((bool) $this->connection()->sismember($userChannelsKey, $channel))->toBeTrue();
});

it('successfully saves second user connection', function () {
Redis::shouldReceive('hexists')->once()->withArgs([
'laravel_database_channels:community:users',
$this->user->getAuthIdentifierForBroadcasting(),
])->andReturn(true);
it('successfully saves second user connection', function () use ($connectionId, $channel) {
$this->repository->join(
$channel,
$this->user,
['email' => $this->user->email],
$connectionId
);

expect($this->repository->join(
'community',
$channel,
$this->user,
['email' => $this->user->email],
'another-connection-id',
))->toBe(false);
))->toBeFalse()->and(
(bool) $this->connection()->sismember(channelMemberKey($channel, $this->user->getAuthIdentifierForBroadcasting(), 'user_sockets'), $connectionId)
)->toBeTrue()->and(
(bool) $this->connection()->sismember(channelMemberKey($channel, $this->user->getAuthIdentifierForBroadcasting(), 'user_sockets'), 'another-connection-id')
)->toBeTrue();
});

it('can remove a user connection from a presence channel', function () use ($connectionId, $channel) {
Redis::shouldReceive('srem')->once()->withArgs([
'laravel_database_channels:community:1:user_sockets',
$connectionId,
])->andReturn(null);

Redis::shouldReceive('scard')->once()->withArgs([
channelMemberKey($channel, '1', 'user_sockets'),
])->andReturn(2);
$this->repository->join($channel, $this->user, ['email' => $this->user->email],'first-connection-id');
$this->repository->join($channel, $this->user, ['email' => $this->user->email], $connectionId);

Redis::shouldReceive('srem')->never();
Redis::shouldReceive('hdel')->never();

expect($this->repository->leave($channel, $this->user, $connectionId))->toBe(false);
expect($this->repository->leave($channel, $this->user, $connectionId))->toBeFalse();
});

it('removes a user from a presence channel list when last connection is removed', function () use ($connectionId, $channel) {
Redis::shouldReceive('srem')->once()->withArgs([
'laravel_database_channels:community:1:user_sockets',
$connectionId,
])->andReturn(1);

Redis::shouldReceive('scard')->once()->withArgs([
channelMemberKey($channel, '1', 'user_sockets'),
])->andReturn(1);

Redis::shouldReceive('srem')->once()->withArgs([
userChannelsKey($this->user),
$channel,
])->andReturn(1);
$this->repository->join($channel, $this->user, ['email' => $this->user->email], $connectionId);

Redis::shouldReceive('hdel')->withArgs([
'laravel_database_channels:community:users',
$this->user->getAuthIdentifierForBroadcasting(),
])->andReturn(null);

expect($this->repository->leave($channel, $this->user, $connectionId))->toBe(true);
expect($this->repository->leave($channel, $this->user, $connectionId))
->toBeTrue()
->and((bool) $this->connection()->exists(channelMemberKey($channel, $this->user->getAuthIdentifierForBroadcasting(), 'user_sockets')))
->toBeFalse();
});

it('will do nothing if a non-existent user tries to leave a presence channel', function () use ($connectionId, $channel) {
expect($this->repository->leave($channel, $this->user, $connectionId))->toBe(false);
});

it('can return all users for a specific channel', function () use ($channel) {
// get hgetall keys for all users in the channel
Redis::shouldReceive('hgetall')
->once()
->with(channelMemberKey($channel, 'users'))
->andReturn([
'1' => json_encode(['email' => $this->user->email]),
'2' => json_encode(['email' => '[email protected]']),
]);
it('can return all users for a specific channel', function () use ($connectionId, $channel) {
$secondUser = User::factory()->create();

$this->repository->join($channel, $this->user, ['email' => $this->user->email], $connectionId);
$this->repository->join($channel, $secondUser, ['email' => $secondUser->email], 'another-connection-id');

$result = $this->repository->getUsers($channel);

expect($result)->toBe([
['email' => $this->user->email],
['email' => '[email protected]'],
['email' => $secondUser->email],
]);
});

it('can handle when there are no users in a specific channel', function () use ($channel) {
Redis::shouldReceive('hgetall')->once()->andReturn([]);

$result = $this->repository->getUsers($channel);

expect($result)->toBe([]);
expect($this->repository->getUsers($channel))->toBe([]);
});

it('can remove a connection from all channels', function () use ($connectionId) {
Redis::shouldReceive('smembers')
->once()
->with(userChannelsKey($this->user))
->andReturn(['channel1', 'channel2']);

Redis::shouldReceive('hget')->once()->withArgs([
channelMemberKey('channel1', 'users'),
$this->user->getAuthIdentifierForBroadcasting(),
])->andReturn(json_encode(['email' => '[email protected]']));

Redis::shouldReceive('hget')->once()->withArgs([
channelMemberKey('channel2', 'users'),
$this->user->getAuthIdentifierForBroadcasting(),
])->andReturn(json_encode(['email' => '[email protected]']));

Redis::shouldReceive('scard')->once()->withArgs([
channelMemberKey('channel1', $this->user->id, 'user_sockets'),
])->andReturn(2);

Redis::shouldReceive('srem')->once()->withArgs([
channelMemberKey('channel1', $this->user->id, 'user_sockets'),
$connectionId,
])->andReturn(1);

Redis::shouldReceive('srem')->never()->withArgs([
userChannelsKey($this->user),
'channel1',
]);

// channel2

Redis::shouldReceive('scard')->once()->withArgs([
channelMemberKey('channel2', $this->user->id, 'user_sockets'),
])->andReturn(1);

Redis::shouldReceive('srem')->once()->withArgs([
channelMemberKey('channel2', $this->user->id, 'user_sockets'),
$connectionId,
])->andReturn(1);

Redis::shouldReceive('srem')->once()->withArgs([
userChannelsKey($this->user),
'channel2',
]);

Redis::shouldReceive('hdel')->once()->withArgs([
channelMemberKey('channel2', 'users'),
$this->user->getAuthIdentifierForBroadcasting(),
])->andReturn(1);
$this->repository->join('channel1', $this->user, ['email' => $this->user->email], $connectionId);
$this->repository->join('channel2', $this->user, ['email' => $this->user->email], $connectionId);

$removedConnections = $this->repository->removeConnection($this->user, $connectionId);

expect($removedConnections)->toEqual([
// [
// 'channel' => 'channel1',
// 'user_info' => ['email' => '[email protected]'],
// ],
[
'channel' => 'channel1',
'user_info' => ['email' => $this->user->email],
],
[
'channel' => 'channel2',
'user_info' => ['email' => '[email protected]'],
'user_info' => ['email' => $this->user->email],
],
]);
});

0 comments on commit 735c972

Please sign in to comment.