From 9b38a06d2f0a3eb0545d8b3115a01c040cde57c6 Mon Sep 17 00:00:00 2001 From: Jon Peterson Date: Mon, 12 Aug 2019 12:20:37 -0600 Subject: [PATCH 01/60] Move game_data_channel specs into folder, shorten filenames --- .../{game_data_channel_spec.rb => game_data_channel/base_spec.rb} | 0 .../guesses_spec.rb} | 0 .../hints_spec.rb} | 0 3 files changed, 0 insertions(+), 0 deletions(-) rename spec/channels/{game_data_channel_spec.rb => game_data_channel/base_spec.rb} (100%) rename spec/channels/{game_data_channel_guesses_spec.rb => game_data_channel/guesses_spec.rb} (100%) rename spec/channels/{game_data_channel_hints_spec.rb => game_data_channel/hints_spec.rb} (100%) diff --git a/spec/channels/game_data_channel_spec.rb b/spec/channels/game_data_channel/base_spec.rb similarity index 100% rename from spec/channels/game_data_channel_spec.rb rename to spec/channels/game_data_channel/base_spec.rb diff --git a/spec/channels/game_data_channel_guesses_spec.rb b/spec/channels/game_data_channel/guesses_spec.rb similarity index 100% rename from spec/channels/game_data_channel_guesses_spec.rb rename to spec/channels/game_data_channel/guesses_spec.rb diff --git a/spec/channels/game_data_channel_hints_spec.rb b/spec/channels/game_data_channel/hints_spec.rb similarity index 100% rename from spec/channels/game_data_channel_hints_spec.rb rename to spec/channels/game_data_channel/hints_spec.rb From 1c7251c7ca49bd9d48523eb136e003338f7668a5 Mon Sep 17 00:00:00 2001 From: Jon Peterson Date: Mon, 12 Aug 2019 12:31:30 -0600 Subject: [PATCH 02/60] Backfill tests for player unsubscribing --- spec/channels/game_data_channel/base_spec.rb | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/spec/channels/game_data_channel/base_spec.rb b/spec/channels/game_data_channel/base_spec.rb index c9081f1..05d27f9 100644 --- a/spec/channels/game_data_channel/base_spec.rb +++ b/spec/channels/game_data_channel/base_spec.rb @@ -10,10 +10,25 @@ end it 'subscribes to a room' do + expect(@player.subscribed?).to eq(false) + subscribe expect(subscription).to be_confirmed expect(subscription).to have_stream_for(game) + + @player.reload + expect(@player.subscribed?).to eq(true) + end + + it 'unsubscribes from a room' do + subscribe + unsubscribe + + expect(subscription).to_not have_stream_for(game) + + @player.reload + expect(@player.subscribed?).to eq(false) end it 'broadcasts joining player info' do From 2811ce283c2d2ee3d6733ffae83128dea90d685b Mon Sep 17 00:00:00 2001 From: Jon Peterson Date: Mon, 12 Aug 2019 12:39:34 -0600 Subject: [PATCH 03/60] Backfill test players can't join game which is over --- spec/channels/game_data_channel/base_spec.rb | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/spec/channels/game_data_channel/base_spec.rb b/spec/channels/game_data_channel/base_spec.rb index 05d27f9..e1cfcc4 100644 --- a/spec/channels/game_data_channel/base_spec.rb +++ b/spec/channels/game_data_channel/base_spec.rb @@ -31,6 +31,14 @@ expect(@player.subscribed?).to eq(false) end + it 'rejects players rejoining after game over' do + game.update_attribute(:over, true) + + subscribe + + expect(subscription).to be_rejected + end + it 'broadcasts joining player info' do expect{ subscribe }.to have_broadcasted_to(game) .from_channel(GameDataChannel) From df8efe7d6967249ba1777edcce1b268fd9729acd Mon Sep 17 00:00:00 2001 From: Jon Peterson Date: Mon, 12 Aug 2019 13:09:46 -0600 Subject: [PATCH 04/60] Add test that player can select a team --- .../player_selections_spec.rb | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 spec/channels/game_data_channel/player_selections_spec.rb diff --git a/spec/channels/game_data_channel/player_selections_spec.rb b/spec/channels/game_data_channel/player_selections_spec.rb new file mode 100644 index 0000000..346a773 --- /dev/null +++ b/spec/channels/game_data_channel/player_selections_spec.rb @@ -0,0 +1,29 @@ +require 'rails_helper' + +describe GameDataChannel, type: :channel do + let(:game){Game.create} + + it 'allows a player to select their team' do + player = game.players.create(user: User.create(name: "Cheryl")) + stub_connection current_player: player + subscription = subscribe + + expect{subscription.select_team({"team" => "red"})} + .to have_broadcasted_to(game) + .from_channel(GameDataChannel) + .once + .with{ |data| + message = JSON.parse(data[:message], symbolize_names: true) + expect(message[:type]).to eq("player-update") + + payload = message[:data] + expect(payload[:id]).to eq(player.id) + expect(payload[:isBlueTeam]).to eq(false) + expect(payload[:isIntel]).to eq(nil) + } + + player.reload + expect(player.red?).to eq(true) + expect(player.intel?).to eq(nil) + end +end From facb2269272f4828d08333f437dbb947558b5818 Mon Sep 17 00:00:00 2001 From: Jon Peterson Date: Mon, 12 Aug 2019 13:21:23 -0600 Subject: [PATCH 05/60] Scaffold pending tests --- .../player_selections_spec.rb | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/spec/channels/game_data_channel/player_selections_spec.rb b/spec/channels/game_data_channel/player_selections_spec.rb index 346a773..d383606 100644 --- a/spec/channels/game_data_channel/player_selections_spec.rb +++ b/spec/channels/game_data_channel/player_selections_spec.rb @@ -26,4 +26,22 @@ expect(player.red?).to eq(true) expect(player.intel?).to eq(nil) end + + xit 'allows a player to select their role' do + end + + xit 'rejects team/role selections once the game has started' do + end + + xit 'rejects a team selection if the team is full' do + end + + xit 'rejects a role selection if there are no more slots available for that role' do + end + + xit 'rejects a team selection if that team already has the sending player\'s role' do + end + + xit 'rejects a role selection if that role is taken for the current team' do + end end From 050d06f3f8543a8eae421aaed87d1808cf741236 Mon Sep 17 00:00:00 2001 From: Jon Peterson Date: Mon, 12 Aug 2019 13:25:01 -0600 Subject: [PATCH 06/60] Add LobbyActions module to GameDataChannel --- app/channels/game_data_channel.rb | 1 + app/channels/lobby_actions.rb | 4 ++++ 2 files changed, 5 insertions(+) create mode 100644 app/channels/lobby_actions.rb diff --git a/app/channels/game_data_channel.rb b/app/channels/game_data_channel.rb index bb7898b..e1a9ce0 100644 --- a/app/channels/game_data_channel.rb +++ b/app/channels/game_data_channel.rb @@ -1,4 +1,5 @@ class GameDataChannel < ApplicationCable::Channel + include LobbyActions on_subscribe :welcome_player def subscribed diff --git a/app/channels/lobby_actions.rb b/app/channels/lobby_actions.rb new file mode 100644 index 0000000..6e0234b --- /dev/null +++ b/app/channels/lobby_actions.rb @@ -0,0 +1,4 @@ +module LobbyActions + def select_team(data) + end +end From 0dcf3ab12f8472aa221b5f9c2b28a4a910c65eb3 Mon Sep 17 00:00:00 2001 From: Jon Peterson Date: Mon, 12 Aug 2019 13:41:48 -0600 Subject: [PATCH 07/60] Add method to select team --- app/channels/lobby_actions.rb | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/app/channels/lobby_actions.rb b/app/channels/lobby_actions.rb index 6e0234b..f0e3992 100644 --- a/app/channels/lobby_actions.rb +++ b/app/channels/lobby_actions.rb @@ -1,4 +1,27 @@ module LobbyActions def select_team(data) + current_player.reload + + if current_player.can_join_team? data["team"] + current_player.team = data["team"] + approve_selection + else + deny_selection(team: data["team"]) + end end + + private + def approve_selection + payload = { + type: "player-update", + data: { + id: current_player.id, + name: current_player.name, + isBlueTeam: current_player.is_blue_team?, + isIntel: current_player.is_intel?, + playerRoster: compose_roster(current_player.game) + } + } + broadcast_message payload + end end From b342ff3adc2f6bf1c1e26be91ddd04d82fcdbd6c Mon Sep 17 00:00:00 2001 From: Jon Peterson Date: Mon, 12 Aug 2019 13:55:04 -0600 Subject: [PATCH 08/60] Add can_join_team method to Player --- app/models/player.rb | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/app/models/player.rb b/app/models/player.rb index e0fb6c9..c28d1a3 100644 --- a/app/models/player.rb +++ b/app/models/player.rb @@ -12,4 +12,17 @@ class Player < ApplicationRecord def name user.name end + + def can_join_team?(team) + players = game.players + players_on_team = players.count{|p| p.team == team} + if players_on_team < game.player_count / 2 + if role && players.count{|p| p.team == team && p.role == role} == 0 + return false + end + return true + else + return false + end + end end From 2359a8da381b855950bcb9141f4d55a129942c70 Mon Sep 17 00:00:00 2001 From: Jon Peterson Date: Mon, 12 Aug 2019 13:57:43 -0600 Subject: [PATCH 09/60] Add is_blue_team? method to Player --- app/models/player.rb | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/app/models/player.rb b/app/models/player.rb index c28d1a3..d1e9305 100644 --- a/app/models/player.rb +++ b/app/models/player.rb @@ -25,4 +25,12 @@ def can_join_team?(team) return false end end + + def is_blue_team? + if team + blue? + else + nil + end + end end From b902b0f7c11e4e8b375f32fa914ea26a491adb08 Mon Sep 17 00:00:00 2001 From: Jon Peterson Date: Mon, 12 Aug 2019 13:57:53 -0600 Subject: [PATCH 10/60] Add is_intel? method to Player --- app/models/player.rb | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/app/models/player.rb b/app/models/player.rb index d1e9305..3993e7a 100644 --- a/app/models/player.rb +++ b/app/models/player.rb @@ -33,4 +33,12 @@ def is_blue_team? nil end end + + def is_intel? + if role + intel? + else + nil + end + end end From 5ceccc8d0c894f301ddb05fcc37f6073759a4843 Mon Sep 17 00:00:00 2001 From: Jon Peterson Date: Mon, 12 Aug 2019 14:02:25 -0600 Subject: [PATCH 11/60] Update current_player on successful selection --- app/channels/lobby_actions.rb | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/app/channels/lobby_actions.rb b/app/channels/lobby_actions.rb index f0e3992..4c5823b 100644 --- a/app/channels/lobby_actions.rb +++ b/app/channels/lobby_actions.rb @@ -4,14 +4,16 @@ def select_team(data) if current_player.can_join_team? data["team"] current_player.team = data["team"] - approve_selection + approve_selection(team: data["team"]) else deny_selection(team: data["team"]) end end private - def approve_selection + def approve_selection(team: nil, role: nil) + current_player.update(team: team) if team + current_player.update(role: role) if role payload = { type: "player-update", data: { From cf92d0a8350ae6711e8805981a94f2f7e5d71e22 Mon Sep 17 00:00:00 2001 From: Jon Peterson Date: Mon, 12 Aug 2019 14:02:44 -0600 Subject: [PATCH 12/60] Fix test accessing role --- spec/channels/game_data_channel/player_selections_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/channels/game_data_channel/player_selections_spec.rb b/spec/channels/game_data_channel/player_selections_spec.rb index d383606..462a14a 100644 --- a/spec/channels/game_data_channel/player_selections_spec.rb +++ b/spec/channels/game_data_channel/player_selections_spec.rb @@ -24,7 +24,7 @@ player.reload expect(player.red?).to eq(true) - expect(player.intel?).to eq(nil) + expect(player.role).to eq(nil) end xit 'allows a player to select their role' do From 6ec66a0e5fa1bd6dec55f7f3df407c292e301694 Mon Sep 17 00:00:00 2001 From: Jon Peterson Date: Mon, 12 Aug 2019 14:04:29 -0600 Subject: [PATCH 13/60] Add test that player can select a role --- .../player_selections_spec.rb | 23 ++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/spec/channels/game_data_channel/player_selections_spec.rb b/spec/channels/game_data_channel/player_selections_spec.rb index 462a14a..6e68280 100644 --- a/spec/channels/game_data_channel/player_selections_spec.rb +++ b/spec/channels/game_data_channel/player_selections_spec.rb @@ -27,7 +27,28 @@ expect(player.role).to eq(nil) end - xit 'allows a player to select their role' do + it 'allows a player to select their role' do + player = game.players.create(user: User.create(name: "Cheryl")) + stub_connection current_player: player + subscription = subscribe + + expect{subscription.select_role({"role" => "intel"})} + .to have_broadcasted_to(game) + .from_channel(GameDataChannel) + .once + .with{ |data| + message = JSON.parse(data[:message], symbolize_names: true) + expect(message[:type]).to eq("player-update") + + payload = message[:data] + expect(payload[:id]).to eq(player.id) + expect(payload[:isBlueTeam]).to eq(nil) + expect(payload[:isIntel]).to eq(true) + } + + player.reload + expect(player.team).to eq(nil) + expect(player.intel?).to eq(true) end xit 'rejects team/role selections once the game has started' do From 4e0e9a81233a052a89b53e1ba7a7f5219aa62db6 Mon Sep 17 00:00:00 2001 From: Jon Peterson Date: Mon, 12 Aug 2019 14:06:28 -0600 Subject: [PATCH 14/60] Add select_role method to LobbyActions --- app/channels/lobby_actions.rb | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/app/channels/lobby_actions.rb b/app/channels/lobby_actions.rb index 4c5823b..e82ab0a 100644 --- a/app/channels/lobby_actions.rb +++ b/app/channels/lobby_actions.rb @@ -10,10 +10,20 @@ def select_team(data) end end + def select_role(data) + current_player.reload + + if current_player.can_join_role? data["role"] + current_player.role = data["role"] + approve_selection(role: data["role"]) + else + deny_selection(role: data["role"]) + end + end + private def approve_selection(team: nil, role: nil) - current_player.update(team: team) if team - current_player.update(role: role) if role + current_player.save payload = { type: "player-update", data: { From 4bdd261285d5bcb2e7e3ca26ada5385d95cbdf7d Mon Sep 17 00:00:00 2001 From: Jon Peterson Date: Mon, 12 Aug 2019 14:13:44 -0600 Subject: [PATCH 15/60] Add can_join_role? method to Player --- app/models/player.rb | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/app/models/player.rb b/app/models/player.rb index 3993e7a..d22c645 100644 --- a/app/models/player.rb +++ b/app/models/player.rb @@ -17,13 +17,31 @@ def can_join_team?(team) players = game.players players_on_team = players.count{|p| p.team == team} if players_on_team < game.player_count / 2 - if role && players.count{|p| p.team == team && p.role == role} == 0 + if role && players.count{|p| p.team == team && p.role == role} > 0 return false end - return true else return false end + return true + end + + def can_join_role?(role) + players = game.players + players_with_role = players.count{|p| p.role == role} + if role == "intel" && players_with_role < 2 + if team && players.count{|p| p.team == team && p.role == role} > 0 + return false + end + elsif role == "spy" && players_with_role < game.player_count - 2 + spies_per_team = (game.player_count - 2) / 2 + if team && players.count{|p| p.team == team && p.role == role} >= spies_per_team + return false + end + else + return false + end + return true end def is_blue_team? From 1935596c0c2a2f91ba2cf047c6bec7b722755f8f Mon Sep 17 00:00:00 2001 From: Jon Peterson Date: Mon, 12 Aug 2019 14:22:22 -0600 Subject: [PATCH 16/60] Add test that player cannot select full team --- .../player_selections_spec.rb | 25 ++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/spec/channels/game_data_channel/player_selections_spec.rb b/spec/channels/game_data_channel/player_selections_spec.rb index 6e68280..d69ff86 100644 --- a/spec/channels/game_data_channel/player_selections_spec.rb +++ b/spec/channels/game_data_channel/player_selections_spec.rb @@ -54,7 +54,30 @@ xit 'rejects team/role selections once the game has started' do end - xit 'rejects a team selection if the team is full' do + it 'rejects a team selection if the team is full' do + archer = game.players.create(user: User.create(name: "Archer"), team: :red) + lana = game.players.create(user: User.create(name: "Lana"), team: :red) + player = game.players.create(user: User.create(name: "Cheryl")) + stub_connection current_player: player + subscription = subscribe + + expect{subscription.select_team({"team" => "red"})} + .to have_broadcasted_to(game) + .from_channel(GameDataChannel) + .once + .with{ |data| + message = JSON.parse(data[:message], symbolize_names: true) + expect(message[:type]).to eq("illegal-action") + + payload = message[:data] + expect(payload[:error]).to eq("The red team is full.") + expect(payload[:category]).to eq("personal") + expect(payload[:byPlayerId]).to eq(player.id) + } + + player.reload + expect(player.team).to eq(nil) + expect(player.role).to eq(nil) end xit 'rejects a role selection if there are no more slots available for that role' do From 5ed47eb5264790e4d87e812b361fec37d87516d4 Mon Sep 17 00:00:00 2001 From: Jon Peterson Date: Mon, 12 Aug 2019 14:22:30 -0600 Subject: [PATCH 17/60] Add test that player cannot select full role --- .../player_selections_spec.rb | 25 ++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/spec/channels/game_data_channel/player_selections_spec.rb b/spec/channels/game_data_channel/player_selections_spec.rb index d69ff86..887029c 100644 --- a/spec/channels/game_data_channel/player_selections_spec.rb +++ b/spec/channels/game_data_channel/player_selections_spec.rb @@ -80,7 +80,30 @@ expect(player.role).to eq(nil) end - xit 'rejects a role selection if there are no more slots available for that role' do + it 'rejects a role selection if there are no more slots available for that role' do + archer = game.players.create(user: User.create(name: "Archer"), role: :intel) + lana = game.players.create(user: User.create(name: "Lana"), role: :intel) + player = game.players.create(user: User.create(name: "Cheryl")) + stub_connection current_player: player + subscription = subscribe + + expect{subscription.select_team({"role" => "intel"})} + .to have_broadcasted_to(game) + .from_channel(GameDataChannel) + .once + .with{ |data| + message = JSON.parse(data[:message], symbolize_names: true) + expect(message[:type]).to eq("illegal-action") + + payload = message[:data] + expect(payload[:error]).to eq("There are already two Intel players.") + expect(payload[:category]).to eq("personal") + expect(payload[:byPlayerId]).to eq(player.id) + } + + player.reload + expect(player.team).to eq(nil) + expect(player.role).to eq(nil) end xit 'rejects a team selection if that team already has the sending player\'s role' do From 11b39e1f8d24a7f9e0dd27de6bbb14fbea1ae45a Mon Sep 17 00:00:00 2001 From: Jon Peterson Date: Mon, 12 Aug 2019 14:38:07 -0600 Subject: [PATCH 18/60] Expect boolean and message returns for selection validators --- app/channels/lobby_actions.rb | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/app/channels/lobby_actions.rb b/app/channels/lobby_actions.rb index e82ab0a..36d3e37 100644 --- a/app/channels/lobby_actions.rb +++ b/app/channels/lobby_actions.rb @@ -1,23 +1,25 @@ module LobbyActions def select_team(data) current_player.reload + approved, message = current_player.can_join_team? data["team"] - if current_player.can_join_team? data["team"] + if approved current_player.team = data["team"] approve_selection(team: data["team"]) else - deny_selection(team: data["team"]) + deny_selection(message) end end def select_role(data) current_player.reload + approved, message = current_player.can_join_role? data["role"] - if current_player.can_join_role? data["role"] + if approved current_player.role = data["role"] approve_selection(role: data["role"]) else - deny_selection(role: data["role"]) + deny_selection(message) end end From bb1d3192fee93cc79b1a8a5f606bb5e066614f11 Mon Sep 17 00:00:00 2001 From: Jon Peterson Date: Mon, 12 Aug 2019 14:38:34 -0600 Subject: [PATCH 19/60] Add messages to validator returns --- app/models/player.rb | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/app/models/player.rb b/app/models/player.rb index d22c645..8051f96 100644 --- a/app/models/player.rb +++ b/app/models/player.rb @@ -18,12 +18,12 @@ def can_join_team?(team) players_on_team = players.count{|p| p.team == team} if players_on_team < game.player_count / 2 if role && players.count{|p| p.team == team && p.role == role} > 0 - return false + return false, "The #{team} team already has a player with the #{role} role." end else - return false + return false, "The #{team} team is full." end - return true + return true, "" end def can_join_role?(role) @@ -31,17 +31,17 @@ def can_join_role?(role) players_with_role = players.count{|p| p.role == role} if role == "intel" && players_with_role < 2 if team && players.count{|p| p.team == team && p.role == role} > 0 - return false + return false, "The #{team} team already has a player with the Intel role." end elsif role == "spy" && players_with_role < game.player_count - 2 spies_per_team = (game.player_count - 2) / 2 if team && players.count{|p| p.team == team && p.role == role} >= spies_per_team - return false + return false, "The #{team} team doesn't have room for more Spy players." end else - return false + return false, "The #{team} team is full." end - return true + return true, "" end def is_blue_team? From 35af76e72927fef2df6fce53f9807be6aa35fe32 Mon Sep 17 00:00:00 2001 From: Jon Peterson Date: Mon, 12 Aug 2019 14:46:55 -0600 Subject: [PATCH 20/60] Correct typo --- spec/channels/game_data_channel/player_selections_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/channels/game_data_channel/player_selections_spec.rb b/spec/channels/game_data_channel/player_selections_spec.rb index 887029c..87255a3 100644 --- a/spec/channels/game_data_channel/player_selections_spec.rb +++ b/spec/channels/game_data_channel/player_selections_spec.rb @@ -87,7 +87,7 @@ stub_connection current_player: player subscription = subscribe - expect{subscription.select_team({"role" => "intel"})} + expect{subscription.select_role({"role" => "intel"})} .to have_broadcasted_to(game) .from_channel(GameDataChannel) .once From ba56aa62c9c2c21b8ee4a653dd945fbc82aefc09 Mon Sep 17 00:00:00 2001 From: Jon Peterson Date: Mon, 12 Aug 2019 14:47:37 -0600 Subject: [PATCH 21/60] Add deny_selection method to LobbyActions --- app/channels/lobby_actions.rb | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/app/channels/lobby_actions.rb b/app/channels/lobby_actions.rb index 36d3e37..1ae9901 100644 --- a/app/channels/lobby_actions.rb +++ b/app/channels/lobby_actions.rb @@ -38,4 +38,16 @@ def approve_selection(team: nil, role: nil) } broadcast_message payload end + + def deny_selection(message) + payload = { + type: "illegal-action", + data: { + error: message, + category: "personal", + byPlayerId: current_player.id + } + } + broadcast_message payload + end end From 5bcd3426977f2bfc01023d7d6f8710b01b6af588 Mon Sep 17 00:00:00 2001 From: Jon Peterson Date: Mon, 12 Aug 2019 14:48:12 -0600 Subject: [PATCH 22/60] Add correct error messaging for spy vs intel role. --- app/models/player.rb | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/app/models/player.rb b/app/models/player.rb index 8051f96..72e403d 100644 --- a/app/models/player.rb +++ b/app/models/player.rb @@ -39,7 +39,11 @@ def can_join_role?(role) return false, "The #{team} team doesn't have room for more Spy players." end else - return false, "The #{team} team is full." + if role == "intel" + return false, "There are already two Intel players." + else + return false, "There is no more room for Spy players." + end end return true, "" end From 3b98e4311b0fd45aab9d57bf807351bd2877c92c Mon Sep 17 00:00:00 2001 From: Jon Peterson Date: Mon, 12 Aug 2019 15:04:54 -0600 Subject: [PATCH 23/60] Add tests that player cannot choose role if full for team --- .../player_selections_spec.rb | 47 ++++++++++++++++++- 1 file changed, 46 insertions(+), 1 deletion(-) diff --git a/spec/channels/game_data_channel/player_selections_spec.rb b/spec/channels/game_data_channel/player_selections_spec.rb index 87255a3..6baea76 100644 --- a/spec/channels/game_data_channel/player_selections_spec.rb +++ b/spec/channels/game_data_channel/player_selections_spec.rb @@ -106,7 +106,52 @@ expect(player.role).to eq(nil) end - xit 'rejects a team selection if that team already has the sending player\'s role' do + it 'rejects a team selection if player is intel and intel role is taken already' do + archer = game.players.create(user: User.create(name: "Archer"), team: :red, role: :intel) + player = game.players.create(user: User.create(name: "Cheryl"), role: :intel) + stub_connection current_player: player + subscription = subscribe + + expect{subscription.select_team({"team" => "red"})} + .to have_broadcasted_to(game) + .from_channel(GameDataChannel) + .once + .with{ |data| + message = JSON.parse(data[:message], symbolize_names: true) + expect(message[:type]).to eq("illegal-action") + + payload = message[:data] + expect(payload[:error]).to eq("The red team already has a player with the Intel role.") + expect(payload[:category]).to eq("personal") + expect(payload[:byPlayerId]).to eq(player.id) + } + + player.reload + expect(player.team).to eq(nil) + end + + it 'rejects a team selection if player is spy and spy role is taken already' do + archer = game.players.create(user: User.create(name: "Archer"), team: :red, role: :spy) + player = game.players.create(user: User.create(name: "Cheryl"), role: :spy) + stub_connection current_player: player + subscription = subscribe + + expect{subscription.select_team({"team" => "red"})} + .to have_broadcasted_to(game) + .from_channel(GameDataChannel) + .once + .with{ |data| + message = JSON.parse(data[:message], symbolize_names: true) + expect(message[:type]).to eq("illegal-action") + + payload = message[:data] + expect(payload[:error]).to eq("The red team doesn't have room for more Spy players.") + expect(payload[:category]).to eq("personal") + expect(payload[:byPlayerId]).to eq(player.id) + } + + player.reload + expect(player.team).to eq(nil) end xit 'rejects a role selection if that role is taken for the current team' do From cbbd57f3cbabb14c4fb3df9ee8981a54d777bb6f Mon Sep 17 00:00:00 2001 From: Jon Peterson Date: Mon, 12 Aug 2019 15:05:38 -0600 Subject: [PATCH 24/60] Refactor counting team/role collisions to private method --- app/models/player.rb | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/app/models/player.rb b/app/models/player.rb index 72e403d..f19d01e 100644 --- a/app/models/player.rb +++ b/app/models/player.rb @@ -63,4 +63,12 @@ def is_intel? nil end end + + private + def collision_count(team, role) + players = game.players + players.count do |player| + player.team == team && player.role == role + end + end end From 45017bb4b202ce260373a497573929a778f6a074 Mon Sep 17 00:00:00 2001 From: Jon Peterson Date: Mon, 12 Aug 2019 15:06:55 -0600 Subject: [PATCH 25/60] Use collision_count method --- app/models/player.rb | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/app/models/player.rb b/app/models/player.rb index f19d01e..d5e86d4 100644 --- a/app/models/player.rb +++ b/app/models/player.rb @@ -17,8 +17,9 @@ def can_join_team?(team) players = game.players players_on_team = players.count{|p| p.team == team} if players_on_team < game.player_count / 2 - if role && players.count{|p| p.team == team && p.role == role} > 0 return false, "The #{team} team already has a player with the #{role} role." + if role == "intel" && collision_count(team, role) > 0 + elsif role == "spy" && collision_count(team, role) >= spies_per_team end else return false, "The #{team} team is full." @@ -30,12 +31,12 @@ def can_join_role?(role) players = game.players players_with_role = players.count{|p| p.role == role} if role == "intel" && players_with_role < 2 - if team && players.count{|p| p.team == team && p.role == role} > 0 + if team && collision_count(team, role) > 0 return false, "The #{team} team already has a player with the Intel role." end elsif role == "spy" && players_with_role < game.player_count - 2 spies_per_team = (game.player_count - 2) / 2 - if team && players.count{|p| p.team == team && p.role == role} >= spies_per_team + if team && collision_count(team, role) >= spies_per_team return false, "The #{team} team doesn't have room for more Spy players." end else From fa486c877eba843ec0921b8642cfd1c3bfc9d684 Mon Sep 17 00:00:00 2001 From: Jon Peterson Date: Mon, 12 Aug 2019 15:07:34 -0600 Subject: [PATCH 26/60] Change error messaging to match expectation --- app/models/player.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/models/player.rb b/app/models/player.rb index d5e86d4..fe0b5f0 100644 --- a/app/models/player.rb +++ b/app/models/player.rb @@ -17,9 +17,11 @@ def can_join_team?(team) players = game.players players_on_team = players.count{|p| p.team == team} if players_on_team < game.player_count / 2 - return false, "The #{team} team already has a player with the #{role} role." + spies_per_team = (game.player_count - 2) / 2 if role == "intel" && collision_count(team, role) > 0 + return false, "The #{team} team already has a player with the Intel role." elsif role == "spy" && collision_count(team, role) >= spies_per_team + return false, "The #{team} team doesn't have room for more Spy players." end else return false, "The #{team} team is full." From 792fc8f30b5455faccf67ebd455b03b297afe360 Mon Sep 17 00:00:00 2001 From: Jon Peterson Date: Mon, 12 Aug 2019 15:12:41 -0600 Subject: [PATCH 27/60] Add test that player cannot join as spy if team spies full --- .../player_selections_spec.rb | 28 ++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/spec/channels/game_data_channel/player_selections_spec.rb b/spec/channels/game_data_channel/player_selections_spec.rb index 6baea76..da3fad7 100644 --- a/spec/channels/game_data_channel/player_selections_spec.rb +++ b/spec/channels/game_data_channel/player_selections_spec.rb @@ -80,7 +80,7 @@ expect(player.role).to eq(nil) end - it 'rejects a role selection if there are no more slots available for that role' do + it 'rejects a role selection if intel and there are no more slots available for that role' do archer = game.players.create(user: User.create(name: "Archer"), role: :intel) lana = game.players.create(user: User.create(name: "Lana"), role: :intel) player = game.players.create(user: User.create(name: "Cheryl")) @@ -106,6 +106,32 @@ expect(player.role).to eq(nil) end + it 'rejects a role selection if spy and there are no more slots available for that role' do + archer = game.players.create(user: User.create(name: "Archer"), role: :spy) + lana = game.players.create(user: User.create(name: "Lana"), role: :spy) + player = game.players.create(user: User.create(name: "Cheryl")) + stub_connection current_player: player + subscription = subscribe + + expect{subscription.select_role({"role" => "spy"})} + .to have_broadcasted_to(game) + .from_channel(GameDataChannel) + .once + .with{ |data| + message = JSON.parse(data[:message], symbolize_names: true) + expect(message[:type]).to eq("illegal-action") + + payload = message[:data] + expect(payload[:error]).to eq("There is no more room for Spy players.") + expect(payload[:category]).to eq("personal") + expect(payload[:byPlayerId]).to eq(player.id) + } + + player.reload + expect(player.team).to eq(nil) + expect(player.role).to eq(nil) + end + it 'rejects a team selection if player is intel and intel role is taken already' do archer = game.players.create(user: User.create(name: "Archer"), team: :red, role: :intel) player = game.players.create(user: User.create(name: "Cheryl"), role: :intel) From 7aa4c2eecdf5a934b8fb4c94a86852bfa7d6210d Mon Sep 17 00:00:00 2001 From: Jon Peterson Date: Mon, 12 Aug 2019 15:14:33 -0600 Subject: [PATCH 28/60] Add test that player cannot join as intel if team intel is full --- .../player_selections_spec.rb | 47 ++++++++++++++++++- 1 file changed, 46 insertions(+), 1 deletion(-) diff --git a/spec/channels/game_data_channel/player_selections_spec.rb b/spec/channels/game_data_channel/player_selections_spec.rb index da3fad7..ec9c77c 100644 --- a/spec/channels/game_data_channel/player_selections_spec.rb +++ b/spec/channels/game_data_channel/player_selections_spec.rb @@ -180,6 +180,51 @@ expect(player.team).to eq(nil) end - xit 'rejects a role selection if that role is taken for the current team' do + it 'rejects a role selection if selection is intel and team already has intel' do + archer = game.players.create(user: User.create(name: "Archer"), team: :red, role: :intel) + player = game.players.create(user: User.create(name: "Cheryl"), team: :red) + stub_connection current_player: player + subscription = subscribe + + expect{subscription.select_role({"role" => "intel"})} + .to have_broadcasted_to(game) + .from_channel(GameDataChannel) + .once + .with{ |data| + message = JSON.parse(data[:message], symbolize_names: true) + expect(message[:type]).to eq("illegal-action") + + payload = message[:data] + expect(payload[:error]).to eq("The red team already has a player with the Intel role.") + expect(payload[:category]).to eq("personal") + expect(payload[:byPlayerId]).to eq(player.id) + } + + player.reload + expect(player.role).to eq(nil) + end + + it 'rejects a role selection if selection is spy and team is full of spies' do + archer = game.players.create(user: User.create(name: "Archer"), team: :red, role: :spy) + player = game.players.create(user: User.create(name: "Cheryl"), team: :red) + stub_connection current_player: player + subscription = subscribe + + expect{subscription.select_role({"role" => "spy"})} + .to have_broadcasted_to(game) + .from_channel(GameDataChannel) + .once + .with{ |data| + message = JSON.parse(data[:message], symbolize_names: true) + expect(message[:type]).to eq("illegal-action") + + payload = message[:data] + expect(payload[:error]).to eq("The red team doesn't have room for more Spy players.") + expect(payload[:category]).to eq("personal") + expect(payload[:byPlayerId]).to eq(player.id) + } + + player.reload + expect(player.role).to eq(nil) end end From e777d2db52201a0e6cc51e8244053c091a18a224 Mon Sep 17 00:00:00 2001 From: Jon Peterson Date: Mon, 12 Aug 2019 15:31:28 -0600 Subject: [PATCH 29/60] Add test that player cannot change team/role after game start --- .../player_selections_spec.rb | 36 ++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/spec/channels/game_data_channel/player_selections_spec.rb b/spec/channels/game_data_channel/player_selections_spec.rb index ec9c77c..fa5ee77 100644 --- a/spec/channels/game_data_channel/player_selections_spec.rb +++ b/spec/channels/game_data_channel/player_selections_spec.rb @@ -51,7 +51,41 @@ expect(player.intel?).to eq(true) end - xit 'rejects team/role selections once the game has started' do + it 'rejects team/role selections once the game has started' do + archer = game.players.create(user: User.create(name: "Archer")) + lana = game.players.create(user: User.create(name: "Lana")) + cyril = game.players.create(user: User.create(name: "Cyril")) + player = game.players.create(user: User.create(name: "Cheryl")) + stub_connection current_player: player + subscription = subscribe + + expect{subscription.select_team({"team" => "red"})} + .to have_broadcasted_to(game) + .from_channel(GameDataChannel) + .once + .with{ |data| + message = JSON.parse(data[:message], symbolize_names: true) + expect(message[:type]).to eq("illegal-action") + + payload = message[:data] + expect(payload[:error]).to eq("Unable to change team. The game has already begun.") + expect(payload[:category]).to eq("personal") + expect(payload[:byPlayerId]).to eq(player.id) + } + + expect{subscription.select_role({"role" => "intel"})} + .to have_broadcasted_to(game) + .from_channel(GameDataChannel) + .once + .with{ |data| + message = JSON.parse(data[:message], symbolize_names: true) + expect(message[:type]).to eq("illegal-action") + + payload = message[:data] + expect(payload[:error]).to eq("Unable to change role. The game has already begun.") + expect(payload[:category]).to eq("personal") + expect(payload[:byPlayerId]).to eq(player.id) + } end it 'rejects a team selection if the team is full' do From 30b68f0104190d7c6c86708ace9cb382ac444117 Mon Sep 17 00:00:00 2001 From: Jon Peterson Date: Mon, 12 Aug 2019 15:32:08 -0600 Subject: [PATCH 30/60] Move error messages to method --- app/models/player.rb | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/app/models/player.rb b/app/models/player.rb index fe0b5f0..8f475e3 100644 --- a/app/models/player.rb +++ b/app/models/player.rb @@ -74,4 +74,15 @@ def collision_count(team, role) player.team == team && player.role == role end end + + def err(snip = nil) + { + team_full: "The #{snip} team is full.", + team_intel_full: "The #{snip} team already has a player with the Intel role.", + team_spies_full: "The #{snip} team doesn't have room for more Spy players.", + intel_full: "There are already two Intel players.", + spies_full: "There is no more room for Spy players.", + game_started: "Unable to change #{snip}. The game has already begun." + } + end end From 11ba9c802114fe8ba5ba99ffd0205e4324aec243 Mon Sep 17 00:00:00 2001 From: Jon Peterson Date: Mon, 12 Aug 2019 15:32:49 -0600 Subject: [PATCH 31/60] Use err method --- app/models/player.rb | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/app/models/player.rb b/app/models/player.rb index 8f475e3..8df5e29 100644 --- a/app/models/player.rb +++ b/app/models/player.rb @@ -19,12 +19,12 @@ def can_join_team?(team) if players_on_team < game.player_count / 2 spies_per_team = (game.player_count - 2) / 2 if role == "intel" && collision_count(team, role) > 0 - return false, "The #{team} team already has a player with the Intel role." + return false, err(team)[:team_intel_full] elsif role == "spy" && collision_count(team, role) >= spies_per_team - return false, "The #{team} team doesn't have room for more Spy players." + return false, err(team)[:team_spies_full] end else - return false, "The #{team} team is full." + return false, err(team)[:team_full] end return true, "" end @@ -34,18 +34,18 @@ def can_join_role?(role) players_with_role = players.count{|p| p.role == role} if role == "intel" && players_with_role < 2 if team && collision_count(team, role) > 0 - return false, "The #{team} team already has a player with the Intel role." + return false, err(team)[:team_intel_full] end elsif role == "spy" && players_with_role < game.player_count - 2 spies_per_team = (game.player_count - 2) / 2 if team && collision_count(team, role) >= spies_per_team - return false, "The #{team} team doesn't have room for more Spy players." + return false, err(team)[:team_spies_full] end else if role == "intel" - return false, "There are already two Intel players." + return false, err[:intel_full] else - return false, "There is no more room for Spy players." + return false, err[:spies_full] end end return true, "" From 3b6422984ef5dcf6372595ece68530247553f0aa Mon Sep 17 00:00:00 2001 From: Jon Peterson Date: Mon, 12 Aug 2019 15:33:15 -0600 Subject: [PATCH 32/60] Reject if game started --- app/models/player.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/models/player.rb b/app/models/player.rb index 8df5e29..4713c8e 100644 --- a/app/models/player.rb +++ b/app/models/player.rb @@ -14,6 +14,7 @@ def name end def can_join_team?(team) + return false, err("team")[:game_started] if game.started? players = game.players players_on_team = players.count{|p| p.team == team} if players_on_team < game.player_count / 2 @@ -30,6 +31,7 @@ def can_join_team?(team) end def can_join_role?(role) + return false, err("role")[:game_started] if game.started? players = game.players players_with_role = players.count{|p| p.role == role} if role == "intel" && players_with_role < 2 From d75f63dc925e1e13fecb98590fdb75b0532be4c6 Mon Sep 17 00:00:00 2001 From: Jon Peterson Date: Mon, 12 Aug 2019 15:33:30 -0600 Subject: [PATCH 33/60] Add method to game to check if started --- app/models/game.rb | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/models/game.rb b/app/models/game.rb index cf66de1..bbffeba 100644 --- a/app/models/game.rb +++ b/app/models/game.rb @@ -96,6 +96,10 @@ def includes_card?(id) card_ids.include? id.to_i end + def started? + game_cards.count > 0 + end + private ###### ### ## ## ######## From df6bea4f6d8674ea073ef9786807c207a7987f61 Mon Sep 17 00:00:00 2001 From: Jon Peterson Date: Mon, 12 Aug 2019 15:45:36 -0600 Subject: [PATCH 34/60] Ensure players subscribed for test --- spec/channels/game_data_channel/player_selections_spec.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/spec/channels/game_data_channel/player_selections_spec.rb b/spec/channels/game_data_channel/player_selections_spec.rb index fa5ee77..3309d20 100644 --- a/spec/channels/game_data_channel/player_selections_spec.rb +++ b/spec/channels/game_data_channel/player_selections_spec.rb @@ -52,9 +52,9 @@ end it 'rejects team/role selections once the game has started' do - archer = game.players.create(user: User.create(name: "Archer")) - lana = game.players.create(user: User.create(name: "Lana")) - cyril = game.players.create(user: User.create(name: "Cyril")) + archer = game.players.create(user: User.create(name: "Archer"), subscribed: true) + lana = game.players.create(user: User.create(name: "Lana"), subscribed: true) + cyril = game.players.create(user: User.create(name: "Cyril"), subscribed: true) player = game.players.create(user: User.create(name: "Cheryl")) stub_connection current_player: player subscription = subscribe From 6f826f8ae64fc43a122d78738c5f19e4ebf75144 Mon Sep 17 00:00:00 2001 From: Jon Peterson Date: Mon, 12 Aug 2019 15:53:42 -0600 Subject: [PATCH 35/60] Add tests that player can still change if current selection is full --- .../player_selections_spec.rb | 48 +++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/spec/channels/game_data_channel/player_selections_spec.rb b/spec/channels/game_data_channel/player_selections_spec.rb index 3309d20..28d26bf 100644 --- a/spec/channels/game_data_channel/player_selections_spec.rb +++ b/spec/channels/game_data_channel/player_selections_spec.rb @@ -261,4 +261,52 @@ player.reload expect(player.role).to eq(nil) end + + it 'does not prevent player from changing team if their current team is full' do + archer = game.players.create(user: User.create(name: "Archer"), team: :red) + player = game.players.create(user: User.create(name: "Cheryl"), team: :red) + stub_connection current_player: player + subscription = subscribe + + expect{subscription.select_team({"team" => "blue"})} + .to have_broadcasted_to(game) + .from_channel(GameDataChannel) + .once + .with{ |data| + message = JSON.parse(data[:message], symbolize_names: true) + expect(message[:type]).to eq("player-update") + + payload = message[:data] + expect(payload[:id]).to eq(player.id) + expect(payload[:isBlueTeam]).to eq(true) + expect(payload[:isIntel]).to eq(nil) + } + + player.reload + expect(player.blue?).to eq(true) + end + + it 'does not prevent player from changing role if their current role is full' do + archer = game.players.create(user: User.create(name: "Archer"), role: :intel) + player = game.players.create(user: User.create(name: "Cheryl"), role: :intel) + stub_connection current_player: player + subscription = subscribe + + expect{subscription.select_role({"role" => "spy"})} + .to have_broadcasted_to(game) + .from_channel(GameDataChannel) + .once + .with{ |data| + message = JSON.parse(data[:message], symbolize_names: true) + expect(message[:type]).to eq("player-update") + + payload = message[:data] + expect(payload[:id]).to eq(player.id) + expect(payload[:isBlueTeam]).to eq(nil) + expect(payload[:isIntel]).to eq(false) + } + + player.reload + expect(player.spy?).to eq(true) + end end From 6c73dddfa9c33e059641c9a356c5ef176048edbd Mon Sep 17 00:00:00 2001 From: Jon Peterson Date: Mon, 12 Aug 2019 16:03:19 -0600 Subject: [PATCH 36/60] Add expectation that team/role appears in player-joined message --- spec/channels/game_data_channel/base_spec.rb | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/spec/channels/game_data_channel/base_spec.rb b/spec/channels/game_data_channel/base_spec.rb index e1cfcc4..1091410 100644 --- a/spec/channels/game_data_channel/base_spec.rb +++ b/spec/channels/game_data_channel/base_spec.rb @@ -48,6 +48,8 @@ payload = message[:data] expect(payload[:id]).to eq(@player.id) expect(payload[:name]).to eq(@player.name) + expect(payload[:isBlueTeam]).to eq(nil) + expect(payload[:isIntel]).to eq(nil) players = payload[:playerRoster] player_ids = [] @@ -55,6 +57,8 @@ players.each do |player| expect(player).to have_key(:id) expect(player).to have_key(:name) + expect(player).to have_key(:isBlueTeam) + expect(player).to have_key(:isIntel) player_ids << player[:id] end From 1b0b6f58529de0b4fc739cfc42e0dd7ba2b1e831 Mon Sep 17 00:00:00 2001 From: Jon Peterson Date: Mon, 12 Aug 2019 16:07:12 -0600 Subject: [PATCH 37/60] Unify compose_players/compose_roster behavior --- app/channels/game_data_channel.rb | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/app/channels/game_data_channel.rb b/app/channels/game_data_channel.rb index e1a9ce0..ec969d2 100644 --- a/app/channels/game_data_channel.rb +++ b/app/channels/game_data_channel.rb @@ -88,22 +88,13 @@ def send_guess(card) ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ###### ####### ## ## ## ####### ###### ######## ## ## ###### - def compose_roster(game) - game.players.map do |p| - { - id: p.id, - name: p.name - } - end - end - def compose_players(game) game.players.map do |p| { id: p.id, name: p.name, - isBlueTeam: p.blue?, - isIntel: p.intel? + isBlueTeam: p.is_blue_team?, + isIntel: p.is_intel? } end end From 736ad03ee07dca182a18f12bc5663620c94da89b Mon Sep 17 00:00:00 2001 From: Jon Peterson Date: Mon, 12 Aug 2019 16:07:58 -0600 Subject: [PATCH 38/60] Use unified compose_players method instead of compose_roster --- app/channels/game_data_channel.rb | 2 +- app/channels/lobby_actions.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/channels/game_data_channel.rb b/app/channels/game_data_channel.rb index ec969d2..7ccaedd 100644 --- a/app/channels/game_data_channel.rb +++ b/app/channels/game_data_channel.rb @@ -135,7 +135,7 @@ def welcome_player data: { id: current_player.id, name: current_player.name, - playerRoster: compose_roster(current_player.game) + playerRoster: compose_players(current_player.game) } } broadcast_message payload diff --git a/app/channels/lobby_actions.rb b/app/channels/lobby_actions.rb index 1ae9901..c356061 100644 --- a/app/channels/lobby_actions.rb +++ b/app/channels/lobby_actions.rb @@ -33,7 +33,7 @@ def approve_selection(team: nil, role: nil) name: current_player.name, isBlueTeam: current_player.is_blue_team?, isIntel: current_player.is_intel?, - playerRoster: compose_roster(current_player.game) + playerRoster: compose_players(current_player.game) } } broadcast_message payload From ed48c10f3ffd28cdeceb4818106524eded20b05d Mon Sep 17 00:00:00 2001 From: Jon Peterson Date: Mon, 12 Aug 2019 16:08:23 -0600 Subject: [PATCH 39/60] Add isBlueTeam and isIntel to player-joined message --- app/channels/game_data_channel.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/channels/game_data_channel.rb b/app/channels/game_data_channel.rb index 7ccaedd..d884c64 100644 --- a/app/channels/game_data_channel.rb +++ b/app/channels/game_data_channel.rb @@ -135,6 +135,8 @@ def welcome_player data: { id: current_player.id, name: current_player.name, + isBlueTeam: current_player.is_blue_team?, + isIntel: current_player.is_intel?, playerRoster: compose_players(current_player.game) } } From 8f35630d5c15291cce85ee2645bd6732d8eac7a0 Mon Sep 17 00:00:00 2001 From: Jon Peterson Date: Mon, 12 Aug 2019 16:33:25 -0600 Subject: [PATCH 40/60] Update player-joined documentation --- README.md | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 3394fc1..37c0512 100644 --- a/README.md +++ b/README.md @@ -284,7 +284,7 @@ HTTP/1.1 401 Unauthorized ### Player Joined -This message is broadcast to the game channel whenever a player joins the game. It contains the name and ID of the player who joined, as well as a roster of all players currently in the game. +This message is broadcast to the game channel whenever a player joins the game. It contains the name, ID, team, and role of the player who joined, as well as a roster of all players currently in the game. ##### Payload @@ -294,10 +294,14 @@ This message is broadcast to the game channel whenever a player joins the game. data: { id: 0, name: "name", + isBlueTeam: true, + isIntel: true, playerRoster: [ { id: 0, - name: "name" + name: "name", + isBlueTeam: true, + isIntel: true, }, ... ] @@ -311,9 +315,13 @@ This message is broadcast to the game channel whenever a player joins the game. |`data` |Object: The data payload of the message.| |`data.id` |Integer: The unique id of the player who joined.| |`data.name` |String: The name of the player who joined.| +|`data.isBlueTeam` |Boolean: `null` if the player has not been assigned a team, `true` if the player is on the blue team, `false` if they're on the red team.| +|`data.isIntel` |Boolean: `null` if the player has not been assigned a role, `true` if the player has the Intel role, `false` if they have the Spy role.| |`data.playerRoster`|Array: A collection of `player` objects for all players currently in the game, **ordered by** the time they joined the lobby.| |`-->player.id` |Integer: The unique id of the given player.| |`-->player.name` |String: The name of the given player.| +|`-->player.isBlueTeam`|Boolean: `null` if the player has not been assigned a team, `true` if the player is on the blue team, `false` if they're on the red team.| +|`-->player.isIntel` |Boolean: `null` if the player has not been assigned a role, `true` if the player has the Intel role, `false` if they have the Spy role.| --- From 08d36d628f2a6d6c4a9281be6bb990f3c204c4d0 Mon Sep 17 00:00:00 2001 From: Jon Peterson Date: Mon, 12 Aug 2019 16:34:00 -0600 Subject: [PATCH 41/60] Add Select Team documentation --- README.md | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/README.md b/README.md index 37c0512..d0a1505 100644 --- a/README.md +++ b/README.md @@ -325,6 +325,23 @@ This message is broadcast to the game channel whenever a player joins the game. --- +### Select Team + +This message is sent from the game client to the server by any player before the game has started. The payload contains the team that the player would like to join. + +##### Call + +```js +cable.selectTeam({ + team: "red" +}) +``` + +|key                                       |Description| +|:--- |:--- | +|`team`|String: The team the player would like to play on. "red" or "blue"| + +--- ### Game Started This message is broadcast to the game channel once the final player has joined the lobby. It is broadcast _after_ the [player joined](#player-joined) message generated by that player. From c80051bb26fc05d90e7ce1f05eb8d67dc1252af2 Mon Sep 17 00:00:00 2001 From: Jon Peterson Date: Mon, 12 Aug 2019 16:34:28 -0600 Subject: [PATCH 42/60] Add Select Role documentation --- README.md | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/README.md b/README.md index d0a1505..f3ead61 100644 --- a/README.md +++ b/README.md @@ -341,6 +341,24 @@ cable.selectTeam({ |:--- |:--- | |`team`|String: The team the player would like to play on. "red" or "blue"| +--- + +### Select Role + +This message is sent from the game client to the server by any player before the game has started. The payload contains the role that the player would like to play. + +##### Call + +```js +cable.selectRole({ + role: "intel" +}) +``` + +|key                                       |Description| +|:--- |:--- | +|`role`|String: The role the player would like to have. "intel" or "spy"| + --- ### Game Started From 40b63dd8b7c4060612182c343f43ce34add887b5 Mon Sep 17 00:00:00 2001 From: Jon Peterson Date: Mon, 12 Aug 2019 16:35:04 -0600 Subject: [PATCH 43/60] Add player-update documentation --- README.md | 44 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/README.md b/README.md index f3ead61..5c14287 100644 --- a/README.md +++ b/README.md @@ -360,6 +360,50 @@ cable.selectRole({ |`role`|String: The role the player would like to have. "intel" or "spy"| --- + +### Player Update + +This message is broadcast to the game channel whenever a player makes a team or role selection. It contains the name, ID, team, and role of the player who changed, as well as a roster of all players currently in the game. + +##### Payload + +```js +{ + type: "player-update", + data: { + id: 0, + name: "name", + isBlueTeam: true, + isIntel: true, + playerRoster: [ + { + id: 0, + name: "name", + isBlueTeam: true, + isIntel: true, + }, + ... + ] + } +} +``` + +|key                                       |Description| +|:--- |:--- | +|`type` |String: The type of message being broadcast.| +|`data` |Object: The data payload of the message.| +|`data.id` |Integer: The unique id of the player who joined.| +|`data.name` |String: The name of the player who joined.| +|`data.isBlueTeam` |Boolean: `null` if the player has not been assigned a team, `true` if the player is on the blue team, `false` if they're on the red team.| +|`data.isIntel` |Boolean: `null` if the player has not been assigned a role, `true` if the player has the Intel role, `false` if they have the Spy role.| +|`data.playerRoster` |Array: A collection of `player` objects for all players currently in the game, **ordered by** the time they joined the lobby.| +|`-->player.id` |Integer: The unique id of the given player.| +|`-->player.name` |String: The name of the given player.| +|`-->player.isBlueTeam`|Boolean: `null` if the player has not been assigned a team, `true` if the player is on the blue team, `false` if they're on the red team.| +|`-->player.isIntel` |Boolean: `null` if the player has not been assigned a role, `true` if the player has the Intel role, `false` if they have the Spy role.| + +--- + ### Game Started This message is broadcast to the game channel once the final player has joined the lobby. It is broadcast _after_ the [player joined](#player-joined) message generated by that player. From cf15e18ce3259e18a9707784cc2de5d23700560e Mon Sep 17 00:00:00 2001 From: Jon Peterson Date: Mon, 12 Aug 2019 16:35:55 -0600 Subject: [PATCH 44/60] Add category to illegal-action documentation --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 5c14287..0fd0653 100644 --- a/README.md +++ b/README.md @@ -602,6 +602,7 @@ This message is broadcast to all players after any illegal action is performed b type: 'illegal-action', data: { error: "", + category: "personal", byPlayerId: 1 } } @@ -612,6 +613,7 @@ This message is broadcast to all players after any illegal action is performed b |`type` |String: The type of message being broadcast.| |`data` |Object: The data payload of the message.| |`data.error` |String: The descriptive error message.| +|`data.category` |String: The category of error. `personal` for messages that should only be displayed to the affected player, `public` for messages that should be broadcast to the lobby, or `info` for messages that should only output to the browser console with `console.warn()`.| |`data.byPlayerId`|Integer: The ID of the player who performed the illegal action.|
The potential illegal actions that are anticipated and caught are: From 998dc650ace789d1bf7f5cb46fd924fa219b42e1 Mon Sep 17 00:00:00 2001 From: Jon Peterson Date: Mon, 12 Aug 2019 16:36:50 -0600 Subject: [PATCH 45/60] Add error messages for player team/role selection to documentation --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 0fd0653..5e8e551 100644 --- a/README.md +++ b/README.md @@ -626,5 +626,7 @@ This message is broadcast to all players after any illegal action is performed b - "\ attempted to submit an invalid hint" - A Spy player submits a guess with a card ID not present in this game - "\ attempted to submit a guess for a card not in this game" +- A Player attempts to select a role or team that is full. + - Various: see [`Player#err`](app/models/player.rb)
From 5a7837905d7ef98c8e886d0a90c497dc8ef4a229 Mon Sep 17 00:00:00 2001 From: Jon Peterson Date: Mon, 12 Aug 2019 16:37:20 -0600 Subject: [PATCH 46/60] Update messaging table in README --- README.md | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 5e8e551..7be0311 100644 --- a/README.md +++ b/README.md @@ -60,14 +60,15 @@ At this point, we have everything we need for our Rails environment. If you wish ## Websockets Message Events -|From Server |From Client | -|:---: |:---: | -|[Player Joined](#player-joined) | | -|[Game Started](#game-started) | | -|[Hint Provided](#hint-provided) |[Hint Sent](#hint-sent) | -|[Board Update](#board-update) |[Guess Sent](#guess-sent)| -|[Game Over](#game-over) | | -|[Illegal Action](#illegal-action)| | +|From Server |From Client | +|:---: |:---: | +|[Player Joined](#player-joined) |[Select Team](#select-team)| +|[Player Update](#player-update) |[Select Role](#select-role)| +|[Game Started](#game-started) | | +|[Hint Provided](#hint-provided) |[Hint Sent](#hint-sent) | +|[Board Update](#board-update) |[Guess Sent](#guess-sent) | +|[Game Over](#game-over) | | +|[Illegal Action](#illegal-action)| | --- From 0ee1fed1b091185316a2a0ec0e0b268372ea41ca Mon Sep 17 00:00:00 2001 From: Jon Peterson Date: Mon, 12 Aug 2019 16:40:56 -0600 Subject: [PATCH 47/60] Left align 'keys' in all tables --- README.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 7be0311..5b8f684 100644 --- a/README.md +++ b/README.md @@ -86,7 +86,7 @@ POST /api/v1/games } ``` |key                                       |description| -|:---: |:--- | +|:--- |:--- | |`name`|String: The username that the requesting user would like to use during the game| ##### Successful Response @@ -102,7 +102,7 @@ HTTP/1.1 201 Created } ``` |key                                       |description| -|:---: |:--- | +|:--- |:--- | |`invite_code`|String: A code which can be shared with other players. They will use this code to join the game.| |`id` |Integer: The unique id for the player.| |`name` |String: A confirmation that the requested name was indeed assigned to the player.| @@ -139,7 +139,7 @@ POST /api/v1/games/:invite_code/players } ``` |key                                       |description| -|:---: |:--- | +|:--- |:--- | |`:invite_code`|String: (Within URI) The invite code provided by the person inviting the requesting user to their existing game.| |`name` |String: The username that the requesting user would like to use during the game| @@ -155,7 +155,7 @@ HTTP/1.1 200 OK } ``` |key                                       |description| -|:---: |:--- | +|:--- |:--- | |`id` |Integer: The unique id for the player.| |`name` |String: A confirmation that the requested name was indeed assigned to the player.| |`token`|String: A token unique to the current player, which can be used to identify them in future requests to the server.| @@ -219,7 +219,7 @@ Request the Intel data for a game, allowing the player to see which cards belong GET /api/v1/intel?token= ``` |key                                       |description| -|:---: |:--- | +|:--- |:--- | |`token`|String: A valid token belonging to a Player with the Intel role.| ##### Successful Response @@ -238,7 +238,7 @@ HTTP/1.1 200 OK } ``` |key                                       |description| -|:---: |:--- | +|:--- |:--- | |`cards` |Array: An **ordered** collection of `card` objects which are part of the game. These cards go onto the board left-to-right, top-to-bottom.| |`-->card.id` |Integer: The unique identifier for the card.| |`-->card.word`|String: The word for the card.| From 66232e557a7e167d2043053b7f490a932b4a337d Mon Sep 17 00:00:00 2001 From: Jon Peterson Date: Tue, 13 Aug 2019 09:50:20 -0600 Subject: [PATCH 48/60] Separate game-setup message from start_game method --- app/channels/game_data_channel.rb | 32 +++++++++++++++++-------------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/app/channels/game_data_channel.rb b/app/channels/game_data_channel.rb index d884c64..93c096f 100644 --- a/app/channels/game_data_channel.rb +++ b/app/channels/game_data_channel.rb @@ -70,6 +70,15 @@ def send_guess(card) end end + def start_game + game = current_player.game + game.reload + if all_players_in?(game) && game.game_cards.count == 0 + game.establish! + game_setup + end + end + private ## ###### ####### ## ## @@ -144,22 +153,17 @@ def welcome_player start_game end - def start_game + def game_setup game = current_player.game - game.reload - if all_players_in?(game) && game.game_cards.count == 0 - game.establish! - - payload = { - type: "game-setup", - data: { - cards: compose_cards(game), - players: compose_players(game), - firstPlayerId: game.current_player.id - } + payload = { + type: "game-setup", + data: { + cards: compose_cards(game), + players: compose_players(game), + firstPlayerId: game.current_player.id } - broadcast_message payload - end + } + broadcast_message payload end def illegal_action(message) From fc0195b88fc2a8c988bcbba7a562d43c760f8351 Mon Sep 17 00:00:00 2001 From: Jon Peterson Date: Tue, 13 Aug 2019 11:16:19 -0600 Subject: [PATCH 49/60] Separate hint-provided message from send_hint method --- app/channels/game_data_channel.rb | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/app/channels/game_data_channel.rb b/app/channels/game_data_channel.rb index 93c096f..3cef6eb 100644 --- a/app/channels/game_data_channel.rb +++ b/app/channels/game_data_channel.rb @@ -34,19 +34,9 @@ def send_hint(hint) num: hint["numCards"].to_i ) - payload = { - type: 'hint-provided', - data: { - isBlueTeam: saved_hint.blue?, - hintWord: saved_hint.word, - relatedCards: saved_hint.num, - currentPlayerId: game.current_player.id - } - } - game.guesses_remaining = saved_hint.num + 1 game.save - broadcast_message payload + provide_hint saved_hint end end @@ -177,6 +167,20 @@ def illegal_action(message) broadcast_message payload end + def provide_hint(hint) + game = current_player.game + payload = { + type: 'hint-provided', + data: { + isBlueTeam: hint.blue?, + hintWord: hint.word, + relatedCards: hint.num, + currentPlayerId: game.current_player.id + } + } + broadcast_message payload + end + def board_update(details) card = compose_card details[:card] payload = { From 18ce6fc2ee043ebdbcfbba3ed3626841a8acbade Mon Sep 17 00:00:00 2001 From: Jon Peterson Date: Tue, 13 Aug 2019 11:38:47 -0600 Subject: [PATCH 50/60] Add expectation that game doesn't start immediately --- .../game_data_channel/game_start_spec.rb | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 spec/channels/game_data_channel/game_start_spec.rb diff --git a/spec/channels/game_data_channel/game_start_spec.rb b/spec/channels/game_data_channel/game_start_spec.rb new file mode 100644 index 0000000..12676eb --- /dev/null +++ b/spec/channels/game_data_channel/game_start_spec.rb @@ -0,0 +1,34 @@ +require 'rails_helper' + +describe GameDataChannel, type: :channel do + let(:user){User.create(name: "Archer")} + let(:game){Game.create} + + before(:each) do + @player = game.players.create(user: user) + stub_connection current_player: @player + end + + it 'does not start game immediately when last player joins' do + subscribe + + player2 = Player.create(game: game, user: User.create(name: "Lana")) + stub_connection current_player: player2 + subscribe + + player3 = Player.create(game: game, user: User.create(name: "Cyril")) + stub_connection current_player: player3 + subscribe + + player4 = Player.create(game: game, user: User.create(name: "Cheryl")) + stub_connection current_player: player4 + + expect{ subscribe }.to have_broadcasted_to(game) + .from_channel(GameDataChannel) + .once # with player-joined, but not with game-setup + .with{ |data| + message = JSON.parse(data[:message], symbolize_names: true) + expect(message[:type]).to eq("player-joined") + } + end +end From 0bcd6d9401fe94cb74b603e601a035697ff67cf9 Mon Sep 17 00:00:00 2001 From: Jon Peterson Date: Tue, 13 Aug 2019 11:40:11 -0600 Subject: [PATCH 51/60] Remove call to start_game from welcome_player --- app/channels/game_data_channel.rb | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/app/channels/game_data_channel.rb b/app/channels/game_data_channel.rb index 3cef6eb..34fd2a5 100644 --- a/app/channels/game_data_channel.rb +++ b/app/channels/game_data_channel.rb @@ -28,7 +28,7 @@ def send_hint(hint) else game.advance! - saved_hint = current_player.game.hints.create( + saved_hint = game.hints.create( team: current_player.team, word: hint["hintWord"], num: hint["numCards"].to_i @@ -140,7 +140,6 @@ def welcome_player } } broadcast_message payload - start_game end def game_setup From 90e20f57a94eb04abc90147949777d1ba9bd91b7 Mon Sep 17 00:00:00 2001 From: Jon Peterson Date: Tue, 13 Aug 2019 11:40:37 -0600 Subject: [PATCH 52/60] Mark test for previous behavior as pending --- spec/channels/game_data_channel/base_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/channels/game_data_channel/base_spec.rb b/spec/channels/game_data_channel/base_spec.rb index 1091410..d30baf3 100644 --- a/spec/channels/game_data_channel/base_spec.rb +++ b/spec/channels/game_data_channel/base_spec.rb @@ -83,7 +83,7 @@ .from_channel(GameDataChannel).once end - it 'broadcasts game start info once all players are in' do + xit 'broadcasts game start info once all players are in' do subscribe player2 = Player.create(game: game, user: User.create(name: "Lana")) From e4cff6b515f4ee3cef60e2866f8e83ced0bb1611 Mon Sep 17 00:00:00 2001 From: Jon Peterson Date: Tue, 13 Aug 2019 11:41:14 -0600 Subject: [PATCH 53/60] Manually establish game in Guess tests --- spec/channels/game_data_channel/guesses_spec.rb | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/spec/channels/game_data_channel/guesses_spec.rb b/spec/channels/game_data_channel/guesses_spec.rb index 8684c0f..ff6d9de 100644 --- a/spec/channels/game_data_channel/guesses_spec.rb +++ b/spec/channels/game_data_channel/guesses_spec.rb @@ -14,6 +14,7 @@ spy = @game.players.create(user: User.create(name: "Cheryl"), role: :spy, team: :red) stub_connection current_player: spy subscription = subscribe + @game.establish! built_player = Player.find(spy.id) built_player.update(role: :spy) @@ -56,6 +57,7 @@ spy = @game.players.create(user: User.create(name: "Cheryl"), role: :intel) stub_connection current_player: spy subscription = subscribe + @game.establish! @game.current_player = Player.where.not(id: spy.id).first @game.guesses_remaining = 1 @@ -81,6 +83,7 @@ random_player = @game.players.create(user: User.create(name: "Cheryl"), role: :intel) stub_connection current_player: random_player subscription = subscribe + @game.establish! built_player = Player.find(random_player.id) built_player.update(role: :intel) @@ -109,6 +112,7 @@ spy = @game.players.create(user: User.create(name: "Cheryl"), role: :spy, team: :red) stub_connection current_player: spy subscription = subscribe + @game.establish! built_player = Player.find(spy.id) built_player.update(role: :spy) @@ -142,6 +146,7 @@ spy = @game.players.create(user: User.create(name: "Cheryl"), role: :spy, team: :red) stub_connection current_player: spy subscription = subscribe + @game.establish! built_player = Player.find(spy.id) built_player.update(role: :spy) @@ -176,6 +181,7 @@ spy = @game.players.create(user: User.create(name: "Cheryl"), role: :spy, team: :red) stub_connection current_player: spy subscription = subscribe + @game.establish! built_player = Player.find(spy.id) built_player.update(role: :spy) @@ -212,6 +218,7 @@ spy = @game.players.create(user: User.create(name: "Cheryl"), role: :spy, team: :red) stub_connection current_player: spy subscription = subscribe + @game.establish! built_player = Player.find(spy.id) built_player.update(role: :spy) @@ -248,6 +255,7 @@ spy = @game.players.create(user: User.create(name: "Cheryl"), role: :spy, team: :red) stub_connection current_player: spy subscription = subscribe + @game.establish! built_player = Player.find(spy.id) built_player.update(role: :spy) @@ -284,6 +292,7 @@ spy = @game.players.create(user: User.create(name: "Cheryl"), role: :spy, team: :red) stub_connection current_player: spy subscription = subscribe + @game.establish! built_player = Player.find(spy.id) built_player.update(role: :spy) @@ -327,6 +336,7 @@ spy = @game.players.create(user: User.create(name: "Cheryl"), role: :spy, team: :red) stub_connection current_player: spy subscription = subscribe + @game.establish! built_player = Player.find(spy.id) built_player.update(role: :spy) @@ -369,6 +379,7 @@ spy = @game.players.create(user: User.create(name: "Cheryl"), role: :spy, team: :red) stub_connection current_player: spy subscription = subscribe + @game.establish! built_player = Player.find(spy.id) built_player.update(role: :spy) From d7722b439641707279335ef066b8fc655aa9146d Mon Sep 17 00:00:00 2001 From: Jon Peterson Date: Tue, 13 Aug 2019 11:42:45 -0600 Subject: [PATCH 54/60] Manually establish game in Player Selection tests --- spec/channels/game_data_channel/player_selections_spec.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/spec/channels/game_data_channel/player_selections_spec.rb b/spec/channels/game_data_channel/player_selections_spec.rb index 28d26bf..90fa51e 100644 --- a/spec/channels/game_data_channel/player_selections_spec.rb +++ b/spec/channels/game_data_channel/player_selections_spec.rb @@ -58,6 +58,7 @@ player = game.players.create(user: User.create(name: "Cheryl")) stub_connection current_player: player subscription = subscribe + game.establish! expect{subscription.select_team({"team" => "red"})} .to have_broadcasted_to(game) From ec995d052fca078541c1c8d35650a0eea16445c4 Mon Sep 17 00:00:00 2001 From: Jon Peterson Date: Tue, 13 Aug 2019 11:43:41 -0600 Subject: [PATCH 55/60] Manually establish game in Hint tests --- spec/channels/game_data_channel/hints_spec.rb | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/spec/channels/game_data_channel/hints_spec.rb b/spec/channels/game_data_channel/hints_spec.rb index 3ef4ece..230a617 100644 --- a/spec/channels/game_data_channel/hints_spec.rb +++ b/spec/channels/game_data_channel/hints_spec.rb @@ -14,6 +14,7 @@ intel = @game.players.create(user: User.create(name: "Cheryl"), role: :intel, team: :red) stub_connection current_player: intel subscription = subscribe + @game.establish! built_player = Player.find(intel.id) built_player.update(role: :intel) @@ -50,6 +51,7 @@ intel = @game.players.create(user: User.create(name: "Cheryl"), role: :intel) stub_connection current_player: intel subscription = subscribe + @game.establish! @game.current_player = Player.where.not(id: intel.id).first @game.save @@ -72,6 +74,7 @@ random_player = @game.players.create(user: User.create(name: "Cheryl"), role: :spy) stub_connection current_player: random_player subscription = subscribe + @game.establish! built_player = Player.find(random_player.id) built_player.update(role: :spy) @@ -98,6 +101,7 @@ intel = @game.players.create(user: User.create(name: "Cheryl"), role: :intel) stub_connection current_player: intel subscription = subscribe + @game.establish! built_player = Player.find(intel.id) built_player.update(role: :intel) From b50076d4c229209fec12b56f7d739fd21c8e007d Mon Sep 17 00:00:00 2001 From: Jon Peterson Date: Tue, 13 Aug 2019 11:47:20 -0600 Subject: [PATCH 56/60] Add test for manual start_game action --- .../game_data_channel/game_start_spec.rb | 56 +++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/spec/channels/game_data_channel/game_start_spec.rb b/spec/channels/game_data_channel/game_start_spec.rb index 12676eb..fd6e889 100644 --- a/spec/channels/game_data_channel/game_start_spec.rb +++ b/spec/channels/game_data_channel/game_start_spec.rb @@ -31,4 +31,60 @@ expect(message[:type]).to eq("player-joined") } end + + it 'broadcasts game start info once all players are in and any player sends start_game action' do + subscribe + + player2 = Player.create(game: game, user: User.create(name: "Lana")) + stub_connection current_player: player2 + subscribe + + player3 = Player.create(game: game, user: User.create(name: "Cyril")) + stub_connection current_player: player3 + subscribe + + player4 = Player.create(game: game, user: User.create(name: "Cheryl")) + stub_connection current_player: player4 + subscription = subscribe + + expect{ subscription.start_game }.to have_broadcasted_to(game) + .from_channel(GameDataChannel) + .once + .with{ |data| + message = JSON.parse(data[:message], symbolize_names: true) + expect(message[:type]).to eq("game-setup") + + payload = message[:data] + expect(payload).to have_key(:cards) + expect(payload[:cards].count).to eq(25) + + payload[:cards].each do |card| + expect(card).to have_key(:id) + expect(card).to have_key(:word) + end + + first_card = GameCard.find(payload[:cards].first[:id]) + expect(first_card.address).to eq(0) + + last_card = GameCard.find(payload[:cards].last[:id]) + expect(last_card.address).to eq(24) + + expect(payload).to have_key(:players) + expect(payload[:players].count).to eq(4) + + player_ids = [] + payload[:players].each do |player| + expect(player).to have_key(:id) + expect(player).to have_key(:name) + expect(player).to have_key(:isBlueTeam) + expect(player).to have_key(:isIntel) + player_ids << player[:id] + end + + player_resources = Player.find(player_ids).to_a + expect(player_resources).to eq(player_resources.sort_by &:updated_at) + + expect(payload).to have_key(:firstPlayerId) + } + end end From ba0e20531454510e6571b09ee4c5e9d67e26487a Mon Sep 17 00:00:00 2001 From: Jon Peterson Date: Tue, 13 Aug 2019 11:47:47 -0600 Subject: [PATCH 57/60] Remove defunct test for old behavior --- spec/channels/game_data_channel/base_spec.rb | 67 -------------------- 1 file changed, 67 deletions(-) diff --git a/spec/channels/game_data_channel/base_spec.rb b/spec/channels/game_data_channel/base_spec.rb index d30baf3..26568c9 100644 --- a/spec/channels/game_data_channel/base_spec.rb +++ b/spec/channels/game_data_channel/base_spec.rb @@ -82,71 +82,4 @@ expect{ subscribe }.to have_broadcasted_to(game) .from_channel(GameDataChannel).once end - - xit 'broadcasts game start info once all players are in' do - subscribe - - player2 = Player.create(game: game, user: User.create(name: "Lana")) - stub_connection current_player: player2 - subscribe - - player3 = Player.create(game: game, user: User.create(name: "Cyril")) - stub_connection current_player: player3 - subscribe - - player4 = Player.create(game: game, user: User.create(name: "Cheryl")) - stub_connection current_player: player4 - - # track number of times game-setup broadcast - game_setup_count = 0 - - expect{ subscribe }.to have_broadcasted_to(game) - .from_channel(GameDataChannel) - .twice # once with player-joined, once with game-setup - .with{ |data| - message = JSON.parse(data[:message], symbolize_names: true) - unless message[:type] == "player-joined" - expect(message[:type]).to eq("game-setup") - # increment count of game-setup messages - game_setup_count += 1 - - payload = message[:data] - expect(payload).to have_key(:cards) - expect(payload[:cards].count).to eq(25) - - payload[:cards].each do |card| - expect(card).to have_key(:id) - expect(card).to have_key(:word) - end - - first_card = GameCard.find(payload[:cards].first[:id]) - expect(first_card.address).to eq(0) - - last_card = GameCard.find(payload[:cards].last[:id]) - expect(last_card.address).to eq(24) - - expect(payload).to have_key(:players) - expect(payload[:players].count).to eq(4) - - player_ids = [] - payload[:players].each do |player| - expect(player).to have_key(:id) - expect(player).to have_key(:name) - expect(player).to have_key(:isBlueTeam) - expect(player).to have_key(:isIntel) - player_ids << player[:id] - end - - player_resources = Player.find(player_ids).to_a - expect(player_resources).to eq(player_resources.sort_by &:updated_at) - - expect(payload).to have_key(:firstPlayerId) - else - # player-joined message should happen once, so allow that to pass - expect(message[:type]).to eq("player-joined") - end - } - # game-setup should have incremented once in the two broadcasts above - expect(game_setup_count).to eq(1) - end end From 76a60a13106647af807ec676e82274dc9576143c Mon Sep 17 00:00:00 2001 From: Jon Peterson Date: Tue, 13 Aug 2019 12:29:22 -0600 Subject: [PATCH 58/60] Update documentation to reflect start_game behavior --- README.md | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 5b8f684..bad0a5d 100644 --- a/README.md +++ b/README.md @@ -64,7 +64,7 @@ At this point, we have everything we need for our Rails environment. If you wish |:---: |:---: | |[Player Joined](#player-joined) |[Select Team](#select-team)| |[Player Update](#player-update) |[Select Role](#select-role)| -|[Game Started](#game-started) | | +|[Game Started](#game-started) |[Start Game](#start-game) | |[Hint Provided](#hint-provided) |[Hint Sent](#hint-sent) | |[Board Update](#board-update) |[Guess Sent](#guess-sent) | |[Game Over](#game-over) | | @@ -405,9 +405,23 @@ This message is broadcast to the game channel whenever a player makes a team or --- +### Start Game + +This message is sent from the game client to the server by any player after all players have joined, while the game is still on the lobby screen. + +##### Call + +```js +cable.startGame() +``` + +No payload should be provided with this message. + +--- + ### Game Started -This message is broadcast to the game channel once the final player has joined the lobby. It is broadcast _after_ the [player joined](#player-joined) message generated by that player. +This message is broadcast to the game channel after all players have joined the lobby and any player sends the [`start_game`](#start-game) message. It is broadcast _after_ the [player joined](#player-joined) message generated by that player. ##### Payload From bc30f15138e9648d9dc903ff30461c695b614f4c Mon Sep 17 00:00:00 2001 From: Jon Peterson Date: Tue, 13 Aug 2019 13:19:35 -0600 Subject: [PATCH 59/60] Add db-query-matchers gem --- Gemfile | 1 + Gemfile.lock | 4 ++++ spec/rails_helper.rb | 16 ++++++++++++++++ 3 files changed, 21 insertions(+) diff --git a/Gemfile b/Gemfile index c907afd..8415008 100644 --- a/Gemfile +++ b/Gemfile @@ -47,6 +47,7 @@ group :development, :test do gem 'awesome_print' gem 'orderly' gem 'action-cable-testing' + gem 'db-query-matchers' end group :development do diff --git a/Gemfile.lock b/Gemfile.lock index 86bd510..c6f8b6e 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -67,6 +67,9 @@ GEM concurrent-ruby (1.1.5) crass (1.0.4) database_cleaner (1.7.0) + db-query-matchers (0.9.0) + activesupport (>= 4.0, <= 6.0) + rspec (~> 3.0) diff-lcs (1.3) docile (1.3.2) erubi (1.8.0) @@ -254,6 +257,7 @@ DEPENDENCIES byebug capybara database_cleaner + db-query-matchers jbuilder (~> 2.5) launchy listen (>= 3.0.5, < 3.2) diff --git a/spec/rails_helper.rb b/spec/rails_helper.rb index feb956c..c4eb4ae 100644 --- a/spec/rails_helper.rb +++ b/spec/rails_helper.rb @@ -97,6 +97,22 @@ end end +DBQueryMatchers.configure do |config| + # config.ignores = [/SHOW TABLES LIKE/] + config.schemaless = true + + # the payload argument is described here: + # http://edgeguides.rubyonrails.org/active_support_instrumentation.html#sql-active-record + # config.on_query_counted do |payload| + # # do something arbitrary with the query + # end + + config.log_backtrace = true + config.backtrace_filter = Proc.new do |backtrace| + backtrace.select { |line| line.start_with?(Rails.root.to_s) } + end +end + require 'database_cleaner' DatabaseCleaner.strategy = :truncation require './db/seeds/cards' From ccb7fa35806be1f3f4327241befdcf0a0e7e97d1 Mon Sep 17 00:00:00 2001 From: Jon Peterson Date: Tue, 13 Aug 2019 13:19:55 -0600 Subject: [PATCH 60/60] Add test to ensure player selections are respected --- .../game_data_channel/game_start_spec.rb | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/spec/channels/game_data_channel/game_start_spec.rb b/spec/channels/game_data_channel/game_start_spec.rb index fd6e889..399252d 100644 --- a/spec/channels/game_data_channel/game_start_spec.rb +++ b/spec/channels/game_data_channel/game_start_spec.rb @@ -87,4 +87,37 @@ expect(payload).to have_key(:firstPlayerId) } end + + it 'respects player team/role selections when present' do + subscribe + + player2 = Player.create(game: game, user: User.create(name: "Lana"), team: :red, role: :intel) + stub_connection current_player: player2 + subscribe + + player3 = Player.create(game: game, user: User.create(name: "Cyril"), team: :red, role: :spy) + stub_connection current_player: player3 + subscribe + + player4 = Player.create(game: game, user: User.create(name: "Cheryl"), team: :blue, role: :intel) + stub_connection current_player: player4 + subscription = subscribe + + expect{ subscription.start_game }.to make_database_queries(count: 1, matching: "UPDATE \"players\"") + .and have_broadcasted_to(game) + .from_channel(GameDataChannel) + .once + .with{ |data| + message = JSON.parse(data[:message], symbolize_names: true) + expect(message[:type]).to eq("game-setup") + payload = message[:data] + + archer = payload[:players].find do |player| + player[:name] == "Archer" + end + + expect(archer[:isBlueTeam]).to eq(true) + expect(archer[:isIntel]).to eq(false) + } + end end