Skip to content

fix: handle_event(next_round) dead-code early returns#2836

Open
immortal71 wants to merge 13 commits intoOWASP:masterfrom
immortal71:fix/2632-next-round-dead-code
Open

fix: handle_event(next_round) dead-code early returns#2836
immortal71 wants to merge 13 commits intoOWASP:masterfrom
immortal71:fix/2632-next-round-dead-code

Conversation

@immortal71
Copy link
Copy Markdown
Contributor

Fixes #2632

The else branch of the outer if round_open? block was closed before Game.find and Endpoint.broadcast, so those three lines executed unconditionally on every call regardless of which branch was taken.

Fix: move Game.find, Endpoint.broadcast, and {:noreply, assign(...)} inside the else branch so the outer if/else is the function's last expression and each branch returns its own value properly.

Copilot AI review requested due to automatic review settings April 19, 2026 13:43
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Fixes handle_event("next_round", ...) in the Player LiveView so the if/else expression becomes the function’s final expression, preventing unintended fallthrough execution (DB fetch + PubSub broadcast) when the handler should have returned early.

Changes:

  • Moves Game.find/1, Endpoint.broadcast/3, and the final {:noreply, ...} return inside the outer else branch of if round_open?(game) to eliminate dead-code early returns.
  • Ensures the no-op branch (round_open? && !can_continue_round?) can return without triggering unnecessary DB queries and broadcasts.

Comment on lines +90 to +94
{:ok, updated_game} = Game.find(game.id)

CopiWeb.Endpoint.broadcast(topic(updated_game.id), "game:updated", updated_game)
CopiWeb.Endpoint.broadcast(topic(updated_game.id), "game:updated", updated_game)

{:noreply, assign(socket, :game, updated_game)}
{:noreply, assign(socket, :game, updated_game)}
Copy link

Copilot AI Apr 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With Game.find/1 + broadcast now only executed in the outer else branch, the round_open?/can_continue_round? branch updates the DB (round_open: false) but returns assign(socket, :game, game) without reloading the updated game. This means the caller’s LiveView state can remain stale until a later PubSub update, and the scheduled :proceed_to_next_round handler will compute rounds_played + 1 from socket.assigns.game (which is now guaranteed stale after this handler). Consider either reloading/broadcasting in the can_continue_round? branch too, or changing handle_info(:proceed_to_next_round, ...) to fetch the latest game from the DB before applying rounds_played updates so it can’t regress/overwrite newer state.

Copilot uses AI. Check for mistakes.
@immortal71
Copy link
Copy Markdown
Contributor Author

@sydseter is this good to go

- Collapse nested with in handle_params to single chain with correct
  {:noreply, redirect} return type (removes 2 dead error branches)
- Replace with/else in handle_info(game:updated) with case; add
  coveralls-ignore on unreachable {:error} arm
- In handle_event(next_round) can_continue? branch: reload game from DB
  before returning so LiveView state reflects round_open: false
  (addresses Copilot stale-state review comment)
- Simplify {:error, changeset} arms in toggle_continue_vote and
  toggle_vote to {:error, _} + Logger.debug; add coveralls-ignore
@immortal71 immortal71 force-pushed the fix/2632-next-round-dead-code branch from 8677e80 to 2d5efa3 Compare April 19, 2026 13:58
@immortal71 immortal71 force-pushed the fix/2632-next-round-dead-code branch from 032e3b2 to ed193f0 Compare April 19, 2026 14:23
@immortal71
Copy link
Copy Markdown
Contributor Author

finally, is this good to go...?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please revert changes.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please revert changes.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please revert changes.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please revert changes.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please revert changes.

# coveralls-ignore-start
{:error, _reason} ->
{:noreply, redirect(socket, to: "/error")}
# coveralls-ignore-stop
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please revert changes.

case Player.find(socket.assigns.player.id) do
{:ok, updated_player} ->
{:noreply, socket |> assign(:game, updated_game) |> assign(:player, updated_player)}
# coveralls-ignore-start
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please revert changes.

# coveralls-ignore-start
{:error, _reason} ->
{:noreply, socket}
# coveralls-ignore-stop
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please revert changes.

CopiWeb.Endpoint.broadcast(topic(updated_game.id), "game:updated", updated_game)
{:noreply, assign(socket, :game, updated_game)}
else
# coveralls-ignore-start
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please revert changes.

# coveralls-ignore-start
Logger.warning("Unauthorized vote attempt: player_id: #{player.id}, dealt_card_id: #{dealt_card_id}, game_id: #{game.id}")
{:noreply, socket |> put_flash(:error, "Invalid card selection")}
# coveralls-ignore-stop
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please revert changes.

@sydseter
Copy link
Copy Markdown
Collaborator

@immortal71 there are also some conflicts

@immortal71
Copy link
Copy Markdown
Contributor Author

@sydseter is this good to go ?

@sydseter
Copy link
Copy Markdown
Collaborator

@immortal71 Please see my comments on your changes. Thank you for your time.

@immortal71
Copy link
Copy Markdown
Contributor Author

@immortal71 Please see my comments on your changes. Thank you for your time.

okayyyyyy

@immortal71
Copy link
Copy Markdown
Contributor Author

@sydseter done !!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Bug: handle_event("next_round") always executes unconditional DB fetch and broadcast — intermediate returns are dead code

3 participants