mirror of
https://github.com/ericasch6/scrum-poker-internal.git
synced 2026-10-13 17:12:08 +00:00
Handle authenticated room instance takeover
This commit is contained in:
@@ -1,7 +1,10 @@
|
||||
<?php
|
||||
|
||||
use App\Enums\RoomStateChange;
|
||||
use App\Events\RoomStateChanged;
|
||||
use App\Models\Room;
|
||||
use App\Models\User;
|
||||
use Illuminate\Support\Facades\Event;
|
||||
use Inertia\Testing\AssertableInertia as Assert;
|
||||
|
||||
function createEmbeddedRoom(array $attributes = []): Room
|
||||
@@ -130,17 +133,81 @@ test('an embedded guest keeps the same player after logging in separately', func
|
||||
});
|
||||
|
||||
test('embedded guest access shows a dedicated error when the display name is taken', function () {
|
||||
$user = User::factory()->create([
|
||||
'email' => 'nico@example.com',
|
||||
]);
|
||||
$room = createEmbeddedRoom();
|
||||
$room->players()->create(['name' => 'Nico']);
|
||||
|
||||
$this->get(route('embedded.room', [
|
||||
$room->players()->create([
|
||||
'user_id' => $user->id,
|
||||
'name' => 'Nico',
|
||||
]);
|
||||
$url = route('embedded.room', [
|
||||
'join_code' => $room->join_code,
|
||||
'player_name' => 'Nico',
|
||||
]))
|
||||
]);
|
||||
|
||||
$this->get($url)
|
||||
->assertUnprocessable()
|
||||
->assertInertia(fn (Assert $page) => $page
|
||||
->component('embedded/Error')
|
||||
->where('message', 'Dieser Anzeigename ist in dem Raum bereits vorhanden.'));
|
||||
->where('message', 'Dieser Anzeigename ist in dem Raum bereits vorhanden.')
|
||||
->where('showLoginAction', true))
|
||||
->assertSessionHas('url.intended', $url);
|
||||
|
||||
$this->post(route('login.store'), [
|
||||
'email' => 'nico@example.com',
|
||||
'password' => 'password',
|
||||
])->assertRedirect($url);
|
||||
|
||||
$this->get($url)
|
||||
->assertOk()
|
||||
->assertInertia(fn (Assert $page) => $page
|
||||
->component('embedded/Room')
|
||||
->where('currentPlayer.name', 'Nico'));
|
||||
});
|
||||
|
||||
test('a new account instance takes over the embedded player and kicks the previous instance', function () {
|
||||
Event::fake([RoomStateChanged::class]);
|
||||
$user = User::factory()->create();
|
||||
$room = createEmbeddedRoom();
|
||||
$player = $room->players()->create([
|
||||
'user_id' => $user->id,
|
||||
'name' => 'Nico',
|
||||
'active_instance_id' => '019b8f80-37c4-71a3-b415-5f2697332c16',
|
||||
]);
|
||||
$url = route('embedded.room', [
|
||||
'join_code' => $room->join_code,
|
||||
'player_name' => 'Nico',
|
||||
]);
|
||||
|
||||
$this->actingAs($user)
|
||||
->withSession([
|
||||
'room_player_instance_id' => '019b8f80-37c4-71a3-b415-5f2697332c17',
|
||||
])
|
||||
->get($url)
|
||||
->assertOk()
|
||||
->assertInertia(fn (Assert $page) => $page
|
||||
->where('currentPlayer.id', $player->id)
|
||||
->where('currentPlayer.instance_id', '019b8f80-37c4-71a3-b415-5f2697332c17'));
|
||||
|
||||
expect($player->fresh()->active_instance_id)
|
||||
->toBe('019b8f80-37c4-71a3-b415-5f2697332c17');
|
||||
|
||||
Event::assertDispatched(
|
||||
RoomStateChanged::class,
|
||||
fn (RoomStateChanged $event): bool => $event->change === RoomStateChange::PlayerInstanceReplaced
|
||||
&& $event->affectedPlayerId === $player->id
|
||||
&& $event->affectedInstanceId === '019b8f80-37c4-71a3-b415-5f2697332c16',
|
||||
);
|
||||
|
||||
$this->withSession([
|
||||
"room_players.{$room->id}" => $player->id,
|
||||
'room_player_instance_id' => '019b8f80-37c4-71a3-b415-5f2697332c16',
|
||||
])
|
||||
->post(route('room.vote.store', $room), ['value' => '5'])
|
||||
->assertRedirect(route('room.join', ['join_code' => $room->join_code]));
|
||||
|
||||
$this->assertDatabaseCount('votes', 0);
|
||||
});
|
||||
|
||||
test('embedded access shows a dedicated error for an unknown room code', function () {
|
||||
@@ -151,7 +218,8 @@ test('embedded access shows a dedicated error for an unknown room code', functio
|
||||
->assertUnprocessable()
|
||||
->assertInertia(fn (Assert $page) => $page
|
||||
->component('embedded/Error')
|
||||
->where('message', 'Dieser Raumcode existiert nicht.'));
|
||||
->where('message', 'Dieser Raumcode existiert nicht.')
|
||||
->where('showLoginAction', false));
|
||||
});
|
||||
|
||||
test('embedded logout returns to the same room as a guest without duplicating the player', function () {
|
||||
|
||||
@@ -45,6 +45,25 @@ test('room state broadcasts synchronously expose sanitized state', function () {
|
||||
]);
|
||||
});
|
||||
|
||||
test('player instance replacement broadcasts the displaced instance', function () {
|
||||
$event = new RoomStateChanged(
|
||||
'019b8f80-37c4-71a3-b415-5f2697332c16',
|
||||
8,
|
||||
RoomStateChange::PlayerInstanceReplaced,
|
||||
null,
|
||||
42,
|
||||
'019b8f80-37c4-71a3-b415-5f2697332c17',
|
||||
);
|
||||
|
||||
expect($event->broadcastWith())->toBe([
|
||||
'version' => 8,
|
||||
'change' => 'player_instance_replaced',
|
||||
'state' => null,
|
||||
'affected_player_id' => 42,
|
||||
'affected_instance_id' => '019b8f80-37c4-71a3-b415-5f2697332c17',
|
||||
]);
|
||||
});
|
||||
|
||||
test('joining a room broadcasts the new room version', function () {
|
||||
Event::fake([RoomStateChanged::class]);
|
||||
$room = createBroadcastRoom();
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
<?php
|
||||
|
||||
use App\Enums\RoomStateChange;
|
||||
use App\Events\RoomStateChanged;
|
||||
use App\Jobs\CloseInactiveRooms;
|
||||
use App\Models\Deck;
|
||||
use App\Models\Room;
|
||||
use App\Models\User;
|
||||
use App\Models\Vote;
|
||||
use Illuminate\Support\Carbon;
|
||||
use Illuminate\Support\Facades\Event;
|
||||
use Inertia\Testing\AssertableInertia as Assert;
|
||||
|
||||
function createLifecycleRoom(array $attributes = []): Room
|
||||
@@ -156,6 +159,36 @@ test('an authenticated player rejoins with the existing room identity', function
|
||||
$response->assertSessionHas("room_players.{$room->id}", $player->id);
|
||||
});
|
||||
|
||||
test('a new account instance takes over the player during a regular room join', function () {
|
||||
$user = User::factory()->create();
|
||||
$room = createLifecycleRoom();
|
||||
$player = $room->players()->create([
|
||||
'user_id' => $user->id,
|
||||
'name' => 'Room nickname',
|
||||
'active_instance_id' => '019b8f80-37c4-71a3-b415-5f2697332c16',
|
||||
]);
|
||||
Event::fake([RoomStateChanged::class]);
|
||||
|
||||
$this->actingAs($user)
|
||||
->withSession([
|
||||
'room_player_instance_id' => '019b8f80-37c4-71a3-b415-5f2697332c17',
|
||||
])
|
||||
->post(route('room.code'), [
|
||||
'join_code' => $room->join_code,
|
||||
'player_name' => 'Room nickname',
|
||||
])
|
||||
->assertRedirect(route('room.show', $room));
|
||||
|
||||
expect($player->fresh()->active_instance_id)
|
||||
->toBe('019b8f80-37c4-71a3-b415-5f2697332c17');
|
||||
|
||||
Event::assertDispatched(
|
||||
RoomStateChanged::class,
|
||||
fn (RoomStateChanged $event): bool => $event->change === RoomStateChange::PlayerInstanceReplaced
|
||||
&& $event->affectedPlayerId === $player->id,
|
||||
);
|
||||
});
|
||||
|
||||
test('an authenticated player can restore room access without the previous session', function () {
|
||||
$user = User::factory()->create();
|
||||
$room = createLifecycleRoom(['owner_id' => $user->id]);
|
||||
|
||||
Reference in New Issue
Block a user