X Tutup
Skip to content

Bug: handle_info in game_live/show.ex always sets requested_round = rounds_played + 1, breaking finished game summary view #2548

@immortal71

Description

@immortal71

Describe the bug

In copi.owasp.org/lib/copi_web/live/game_live/show.ex, the handle_info/2 callback that processes game:updated broadcasts always sets requested_round to updated_game.rounds_played + 1 — regardless of whether the game has finished. This contradicts handle_params/3, which correctly uses rounds_played (without +1) for finished games. As a result, any game:updated broadcast received while viewing a finished game summary resets the view to a non-existent round.

Affected file

copi.owasp.org/lib/copi_web/live/game_live/show.ex

Code snippet

# handle_params — CORRECTLY handles finished games (line 24-28):
current_round = if game.finished_at do
  game.rounds_played          # ← correct: last actual round
else
  game.rounds_played + 1      # ← correct: current in-progress round
end

# handle_info — ALWAYS does +1, ignores finished_at (line 48-51):
def handle_info(%{topic: message_topic, event: "game:updated", payload: updated_game}, socket) do
  cond do
    topic(updated_game.id) == message_topic ->
      {:noreply, assign(socket, :game, updated_game) |> assign(:requested_round, updated_game.rounds_played + 1)}
      # ↑ BUG: adds +1 even when updated_game.finished_at is set
    true ->
      {:noreply, socket}
  end
end

Exact failure scenario

  1. Game finishes with rounds_played = N and finished_at set.
  2. handle_params correctly renders the game summary with requested_round = N.
  3. Any subsequent game:updated broadcast fires (e.g., from a late-arriving player reconnect, the transition broadcast itself, or any other event).
  4. handle_info sets requested_round = N + 1 — a round that does not exist.
  5. The game summary view now tries to show round N + 1, finds no cards, and renders a blank/broken summary page.

Expected behavior

handle_info should mirror handle_params in how it computes requested_round:

def handle_info(%{topic: message_topic, event: "game:updated", payload: updated_game}, socket) do
  cond do
    topic(updated_game.id) == message_topic ->
      requested_round =
        if updated_game.finished_at do
          updated_game.rounds_played
        else
          updated_game.rounds_played + 1
        end
      {:noreply, assign(socket, :game, updated_game) |> assign(:requested_round, requested_round)}
    true ->
      {:noreply, socket}
  end
end

Steps to reproduce

  1. Create and play a game to completion (all rounds done, finished_at set).
  2. While on the game summary page (/games/:game_id), trigger any action that causes a game:updated broadcast (or simply wait if there is any background activity).
  3. Observe: the summary page goes blank / shows "round not found" because requested_round is now one past the last round.

Additional context

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions

      X Tutup