Skip to content

Commit 29f531d

Browse files
committed
Fix tests
1 parent 13e008b commit 29f531d

11 files changed

Lines changed: 131 additions & 32 deletions

File tree

apps/codebattle/assets/js/widgets/middlewares/GroupTournament.js

Lines changed: 21 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,21 @@ export const setTournamentChannel = (tournamentId) => {
1212
return channel.setupChannel(newChannelName);
1313
};
1414

15+
const applyInviteUpdate = (dispatch, payload) => {
16+
const normalizedData = camelizeKeys(payload);
17+
const invite = normalizedData.invite;
18+
const externalSetup = normalizedData.externalSetup;
19+
const platformError = normalizedData.platformError || null;
20+
21+
dispatch(actions.updateInviteState(invite.state));
22+
23+
if (invite.inviteLink) {
24+
dispatch(actions.updateInviteLink(invite.inviteLink));
25+
}
26+
27+
dispatch(actions.updateGroupTournamentData({ externalSetup, platformError }));
28+
};
29+
1530
export const connectToTournament = (currentUserId) => (dispatch) => {
1631
if (!channel) {
1732
console.error("Channel not initialized");
@@ -84,6 +99,11 @@ export const connectToTournament = (currentUserId) => (dispatch) => {
8499
}
85100
};
86101

102+
const handleInviteUpdated = (response) => {
103+
applyInviteUpdate(dispatch, response);
104+
};
105+
106+
channel.addListener("group_tournament:invite_updated", handleInviteUpdated);
87107
channel.addListener(channelTopics.groupTournamentRunUpdated, handleRunUpdated);
88108

89109
return channel;
@@ -98,18 +118,7 @@ export const requestInviteUpdate = () => (dispatch) => {
98118
channel
99119
.push(channelMethods.requestInviteUpdate, {})
100120
.receive("ok", (data) => {
101-
const normalizedData = camelizeKeys(data);
102-
const invite = normalizedData.invite;
103-
const externalSetup = normalizedData.externalSetup;
104-
const platformError = normalizedData.platformError || null;
105-
106-
dispatch(actions.updateInviteState(invite.state));
107-
108-
if (invite.inviteLink) {
109-
dispatch(actions.updateInviteLink(invite.inviteLink));
110-
}
111-
112-
dispatch(actions.updateGroupTournamentData({ externalSetup, platformError }));
121+
applyInviteUpdate(dispatch, data);
113122
})
114123
.receive("error", (error) => {
115124
console.error("Request invite update failed", error);

apps/codebattle/lib/codebattle/external_platform_invite/advancer.ex

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,18 +12,31 @@ defmodule Codebattle.ExternalPlatformInvite.Advancer do
1212

1313
@spec advance(map(), map()) :: map()
1414
def advance(invite, user) do
15+
advance(invite, user, 3)
16+
end
17+
18+
defp advance(invite, _user, 0), do: invite
19+
20+
defp advance(invite, user, attempts_left) do
1521
alias_name = invite_alias(user)
1622

1723
case invite.state do
18-
"pending" -> try_advance(InviteContext.send_invite(invite, alias_name), invite)
19-
"creating" -> try_advance(InviteContext.poll_status(invite), invite)
20-
"invited" -> try_advance(InviteContext.check_accepted(invite, invite_login(user)), invite)
24+
"pending" -> try_advance(InviteContext.send_invite(invite, alias_name), invite, user, attempts_left)
25+
"creating" -> try_advance(InviteContext.poll_status(invite), invite, user, attempts_left)
26+
"invited" -> try_advance(InviteContext.check_accepted(invite, invite_login(user)), invite, user, attempts_left)
2127
_ -> invite
2228
end
2329
end
2430

25-
defp try_advance({:ok, updated}, _invite), do: updated
26-
defp try_advance({:error, _}, invite), do: invite
31+
defp try_advance({:ok, updated}, invite, user, attempts_left) do
32+
if updated.state == invite.state do
33+
updated
34+
else
35+
advance(updated, user, attempts_left - 1)
36+
end
37+
end
38+
39+
defp try_advance({:error, _}, invite, _user, _attempts_left), do: invite
2740

2841
defp invite_alias(%{external_oauth_login: login}) when is_binary(login) and login != "", do: login
2942
defp invite_alias(%{name: name}) when is_binary(name) and name != "", do: name

apps/codebattle/lib/codebattle/group_task/context.ex

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -417,9 +417,12 @@ defmodule Codebattle.GroupTask.Context do
417417
end
418418

419419
defp do_update_runs(runs, status, result) do
420+
ranking = extract_ranking(result)
421+
420422
Repo.transaction(fn ->
421423
runs
422-
|> Enum.reduce_while([], fn run, acc -> update_run(run, acc, status, result) end)
424+
|> Repo.preload(:user_group_tournament)
425+
|> Enum.reduce_while([], fn run, acc -> update_run(run, acc, status, result, ranking) end)
423426
|> Enum.reverse()
424427
end)
425428
end
@@ -441,13 +444,28 @@ defmodule Codebattle.GroupTask.Context do
441444
|> maybe_continue_transaction(acc)
442445
end
443446

444-
defp update_run(run, acc, status, result) do
447+
defp update_run(run, acc, status, result, ranking) do
448+
user_id = run.user_group_tournament.user_id
449+
score = extract_score_for_user(ranking, user_id)
450+
445451
run
446-
|> UserGroupTournamentRun.changeset(%{status: status, result: result})
452+
|> UserGroupTournamentRun.changeset(%{status: status, result: result, score: score})
447453
|> Repo.update()
448454
|> maybe_continue_transaction(acc)
449455
end
450456

457+
defp extract_ranking(%{"summary" => %{"ranking" => ranking}}) when is_list(ranking), do: ranking
458+
defp extract_ranking(_), do: []
459+
460+
defp extract_score_for_user([], _user_id), do: nil
461+
462+
defp extract_score_for_user(ranking, user_id) do
463+
case Enum.find(ranking, fn entry -> entry["player_id"] == user_id end) do
464+
%{"score" => score} when is_integer(score) -> score
465+
_ -> nil
466+
end
467+
end
468+
451469
defp maybe_continue_transaction({:ok, run}, acc), do: {:cont, [run | acc]}
452470
defp maybe_continue_transaction({:error, changeset}, _acc), do: Repo.rollback(changeset)
453471

apps/codebattle/lib/codebattle/tournament/strategy/base.ex

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1327,7 +1327,7 @@ defmodule Codebattle.Tournament.Base do
13271327
tournament
13281328
end
13291329

1330-
defp maybe_save_event_results(%{event_id: _event_id} = tournament) do
1330+
defp maybe_save_event_results(%{event_id: event_id} = tournament) when not is_nil(event_id) do
13311331
Codebattle.UserEvent.Stage.Context.save_tournament_results_async(tournament.id)
13321332
tournament
13331333
end

apps/codebattle/lib/codebattle/user_group_tournament_run.ex

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ defmodule Codebattle.UserGroupTournamentRun do
2222
field(:player_ids, {:array, :integer}, default: [])
2323
field(:status, :string)
2424
field(:result, :map, default: %{})
25+
field(:score, :integer)
2526

2627
timestamps()
2728
end
@@ -35,7 +36,8 @@ defmodule Codebattle.UserGroupTournamentRun do
3536
:run_key,
3637
:player_ids,
3738
:status,
38-
:result
39+
:result,
40+
:score
3941
])
4042
|> validate_required([
4143
:user_group_tournament_id,

apps/codebattle/lib/codebattle_web/channels/group_tournament_channel.ex

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@ defmodule CodebattleWeb.GroupTournamentChannel do
4141
{user, platform_error} = maybe_ensure_platform_credentials(current_user, invite, group_tournament)
4242
external_setup = maybe_ensure_external_setup(user, group_tournament, invite)
4343

44+
maybe_schedule_invite_refresh(socket, tournament_id, invite)
45+
4446
{:ok,
4547
%{
4648
status: group_tournament.state,
@@ -76,6 +78,32 @@ defmodule CodebattleWeb.GroupTournamentChannel do
7678
{:noreply, socket}
7779
end
7880

81+
def handle_info({:refresh_invite, tournament_id, retries_left}, socket) do
82+
current_user = socket.assigns.current_user
83+
group_tournament = GroupTournamentContext.get_group_tournament!(tournament_id)
84+
85+
invite =
86+
current_user.id
87+
|> InviteContext.get_or_create_invite(tournament_id)
88+
|> InviteAdvancer.advance(current_user)
89+
90+
if invite.state in ["creating", "pending"] do
91+
maybe_schedule_invite_refresh(socket, tournament_id, invite, retries_left)
92+
{:noreply, socket}
93+
else
94+
{user, platform_error} = maybe_ensure_platform_credentials(current_user, invite, group_tournament)
95+
external_setup = maybe_ensure_external_setup(user, group_tournament, invite)
96+
97+
push(socket, "group_tournament:invite_updated", %{
98+
invite: serialize_invite(invite),
99+
platform_error: platform_error,
100+
external_setup: serialize_external_setup(external_setup, user, group_tournament)
101+
})
102+
103+
{:noreply, socket}
104+
end
105+
end
106+
79107
def handle_info(_message, socket) do
80108
{:noreply, socket}
81109
end
@@ -89,6 +117,9 @@ defmodule CodebattleWeb.GroupTournamentChannel do
89117
|> InviteAdvancer.advance(current_user)
90118

91119
{user, platform_error} = maybe_ensure_platform_credentials(current_user, invite, group_tournament)
120+
response = serialize_invite_reply(user, group_tournament, invite, platform_error)
121+
122+
maybe_schedule_invite_refresh(socket, tournament_id, invite)
92123

93124
socket =
94125
if invite.state == "accepted" do
@@ -97,7 +128,7 @@ defmodule CodebattleWeb.GroupTournamentChannel do
97128
socket
98129
end
99130

100-
{:reply, {:ok, serialize_invite_reply(user, group_tournament, invite, platform_error)}, socket}
131+
{:reply, {:ok, response}, socket}
101132
end
102133

103134
defp extract_tournament_id("group_tournament:" <> tournament_id) do
@@ -205,4 +236,15 @@ defmodule CodebattleWeb.GroupTournamentChannel do
205236
_ -> :error
206237
end
207238
end
239+
240+
defp maybe_schedule_invite_refresh(socket, tournament_id, invite),
241+
do: maybe_schedule_invite_refresh(socket, tournament_id, invite, 15)
242+
243+
defp maybe_schedule_invite_refresh(socket, tournament_id, %{state: state}, retries_left)
244+
when state in ["creating", "pending"] and retries_left > 0 do
245+
Process.send_after(self(), {:refresh_invite, tournament_id, retries_left - 1}, 1_000)
246+
socket
247+
end
248+
249+
defp maybe_schedule_invite_refresh(socket, _tournament_id, _invite, _retries_left), do: socket
208250
end
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
defmodule Codebattle.Repo.Migrations.AddScoreToUserGroupTournamentRuns do
2+
@moduledoc false
3+
use Ecto.Migration
4+
5+
def change do
6+
alter table(:user_group_tournament_runs) do
7+
add(:score, :integer)
8+
end
9+
end
10+
end

apps/codebattle/test/codebattle/external_platform_test.exs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,13 @@ defmodule Codebattle.ExternalPlatformTest do
44
alias Codebattle.ExternalPlatform
55

66
setup do
7+
original_adapter = Application.get_env(:codebattle, :external_platform_adapter)
78
original_service_url = Application.get_env(:codebattle, :external_platform_service_url)
89
original_org_slug = Application.get_env(:codebattle, :external_platform_org_slug)
910
original_auth_req_options = Application.get_env(:codebattle, :auth_req_options)
1011

1112
on_exit(fn ->
13+
Application.put_env(:codebattle, :external_platform_adapter, original_adapter)
1214
Application.put_env(:codebattle, :external_platform_service_url, original_service_url)
1315
Application.put_env(:codebattle, :external_platform_org_slug, original_org_slug)
1416
Application.put_env(:codebattle, :auth_req_options, original_auth_req_options)
@@ -18,6 +20,7 @@ defmodule Codebattle.ExternalPlatformTest do
1820
end
1921

2022
test "create_invite returns an error tuple when external platform url is invalid" do
23+
Application.put_env(:codebattle, :external_platform_adapter, nil)
2124
Application.put_env(:codebattle, :external_platform_service_url, "value")
2225
Application.put_env(:codebattle, :external_platform_org_slug, "value")
2326
Application.put_env(:codebattle, :auth_req_options, [])

apps/codebattle/test/codebattle/user_group_tournament/context_test.exs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ defmodule Codebattle.UserGroupTournament.ContextTest do
6363
assert record.repo_state == "completed"
6464
assert record.role_state == "completed"
6565
assert record.secret_state == "completed"
66-
assert record.repo_url == "https://external.platform/test-org/source-repo-ext-user"
66+
assert record.repo_url == "https://fake-platform.test/test-org/source-repo-ext-user"
6767
assert Context.repo_slug_for(user, group_tournament) == "source-repo-ext-user"
6868
assert record.secret_key == "CODEBATTLE_AUTH_TOKEN"
6969
assert record.secret_group == "ci"

apps/codebattle/test/codebattle_web/channels/group_tournament_channel_test.exs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,16 +30,18 @@ defmodule CodebattleWeb.GroupTournamentChannelTest do
3030
{:ok, %{socket: socket, topic: topic, user: user}}
3131
end
3232

33-
test "join sends a pending invite and returns it", %{socket: socket, topic: topic, user: user} do
33+
test "join returns a ready invite and returns it", %{socket: socket, topic: topic, user: user} do
3434
{:ok, response, _socket} = subscribe_and_join(socket, GroupTournamentChannel, topic)
3535

36-
assert %{invite: %{state: "creating", response: %{"operation_id" => "op-123"}}} = response
36+
assert %{invite: %{state: "invited", invite_link: invite_link}} = response
37+
assert String.starts_with?(invite_link, "https://fake-platform.test/invite/")
3738

3839
tournament_id = topic |> String.split(":") |> List.last() |> String.to_integer()
3940
invite = InviteContext.get_invite(user.id, tournament_id)
4041

41-
assert invite.state == "creating"
42-
assert invite.operation_id == "op-123"
42+
assert invite.state == "invited"
43+
assert String.starts_with?(invite.operation_id, "fake-op-")
44+
assert String.starts_with?(invite.invite_link, "https://fake-platform.test/invite/")
4345
end
4446

4547
test "join rejects an invalid tournament id", %{socket: socket} do

0 commit comments

Comments
 (0)