'Fillibri Planning', 'deck_config' => [ 'name' => 'Fibonacci', 'cards' => [ ['key' => '3', 'label' => '3'], ['key' => '5', 'label' => '5'], ], ], ...$attributes, ]); } test('a guest automatically joins the embedded room', function () { $room = createEmbeddedRoom(); $response = $this->get(route('embedded.room', [ 'join_code' => strtolower($room->join_code), 'player_name' => ' Nico ', ])) ->assertOk() ->assertInertia(fn (Assert $page) => $page ->component('embedded/Room') ->where('room.id', $room->id) ->where('currentPlayer.name', 'Nico')); $player = $room->players()->sole(); expect($player->user_id)->toBeNull() ->and($player->is_moderator)->toBeTrue(); $response->assertSessionHas("room_players.{$room->id}", $player->id); }); test('embedded guest access reuses the session player and applies a changed display name', function () { $room = createEmbeddedRoom(); $player = $room->players()->create([ 'name' => 'Old name', ]); $this->withSession(["room_players.{$room->id}" => $player->id]) ->get(route('embedded.room', [ 'join_code' => $room->join_code, 'player_name' => 'New name', ])) ->assertOk(); expect($room->players()->count())->toBe(1) ->and($player->fresh()->name)->toBe('New name'); }); test('an authenticated user automatically joins the embedded room', function () { $user = User::factory()->create(); $room = createEmbeddedRoom(['allow_participants_to_manage_rounds' => true]); $this->actingAs($user) ->get(route('embedded.room', [ 'join_code' => strtolower($room->join_code), 'player_name' => ' Nico ', ])) ->assertOk() ->assertInertia(fn (Assert $page) => $page ->component('embedded/Room') ->where('room.id', $room->id) ->where('room.allow_participants_to_manage_rounds', true) ->where('currentPlayer.name', 'Nico')) ->assertSessionHas("room_players.{$room->id}"); $this->assertDatabaseHas('players', [ 'room_id' => $room->id, 'user_id' => $user->id, 'name' => 'Nico', 'is_moderator' => true, ]); }); test('a persistent room owner regains moderation through the embedded room', function () { $owner = User::factory()->create(); $room = createEmbeddedRoom([ 'owner_id' => $owner->id, 'is_persistent' => true, ]); $oldModerator = $room->players()->create([ 'user_id' => $owner->id, 'name' => 'Nico', 'is_moderator' => true, 'last_seen_at' => now()->subHour(), ]); app()->call([new RemoveAbsentPlayers, 'handle']); $this->actingAs($owner) ->withSession(["room_players.{$room->id}" => $oldModerator->id]) ->get(route('embedded.room', [ 'join_code' => $room->join_code, 'player_name' => 'Nico', ])) ->assertOk() ->assertInertia(fn (Assert $page) => $page ->where('currentPlayer.is_moderator', true)); expect($room->players()->where('user_id', $owner->id)->sole()->id)->not->toBe($oldModerator->id); }); test('a persistent room owner does not take over another account session when becoming moderator', function () { $owner = User::factory()->create(); $otherUser = User::factory()->create(); $room = createEmbeddedRoom([ 'owner_id' => $owner->id, 'is_persistent' => true, ]); $otherPlayer = $room->players()->create([ 'user_id' => $otherUser->id, 'name' => 'Anna', ]); $this->actingAs($owner) ->withSession(["room_players.{$room->id}" => $otherPlayer->id]) ->get(route('embedded.room', [ 'join_code' => $room->join_code, 'player_name' => 'Nico', ])) ->assertOk() ->assertInertia(fn (Assert $page) => $page ->where('currentPlayer.is_moderator', true)); expect($otherPlayer->fresh()->is_moderator)->toBeFalse() ->and($room->players()->where('user_id', $owner->id)->sole()->is_moderator)->toBeTrue(); }); test('embedded access reuses the user player and applies a changed display name', function () { $user = User::factory()->create(); $room = createEmbeddedRoom(); $player = $room->players()->create([ 'user_id' => $user->id, 'name' => 'Old name', ]); $this->actingAs($user) ->get(route('embedded.room', [ 'join_code' => $room->join_code, 'player_name' => 'New name', ])) ->assertOk(); expect($room->players()->count())->toBe(1) ->and($player->fresh()->name)->toBe('New name'); }); test('an embedded guest keeps the same player after logging in separately', function () { $user = User::factory()->create([ 'email' => 'nico@example.com', ]); $room = createEmbeddedRoom(); $url = route('embedded.room', [ 'join_code' => $room->join_code, 'player_name' => 'Nico', ]); $this->get($url)->assertOk(); $player = $room->players()->sole(); $this->post(route('login.store'), [ 'email' => 'nico@example.com', 'password' => 'password', ])->assertRedirect(); $this->get($url)->assertOk(); expect($room->players()->count())->toBe(1) ->and($player->fresh()->user_id)->toBe($user->id); }); 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([ '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('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 () { $this->get(route('embedded.room', [ 'join_code' => 'ABC234', 'player_name' => 'Nico', ])) ->assertUnprocessable() ->assertInertia(fn (Assert $page) => $page ->component('embedded/Error') ->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 () { $user = User::factory()->create(); $room = createEmbeddedRoom(); $url = route('embedded.room', [ 'join_code' => $room->join_code, 'player_name' => 'Nico', ]); $this->actingAs($user)->get($url)->assertOk(); $player = $room->players()->sole(); $this ->followingRedirects() ->post(route('embedded.logout', [ 'join_code' => $room->join_code, 'player_name' => 'Nico', ])) ->assertOk() ->assertInertia(fn (Assert $page) => $page ->component('embedded/Room') ->where('currentPlayer.id', $player->id)); $this->assertGuest() ->assertDatabaseCount('players', 1); });