mirror of
https://github.com/fluxerapp/fluxer
synced 2026-10-07 19:22:14 +09:00
829 lines
30 KiB
Erlang
829 lines
30 KiB
Erlang
%% SPDX-License-Identifier: AGPL-3.0-or-later
|
|
|
|
-module(session_ready_tests).
|
|
-typing([eqwalizer]).
|
|
|
|
-include_lib("eunit/include/eunit.hrl").
|
|
|
|
process_guild_state_unavailable_dispatches_guild_delete_test() ->
|
|
State0 = base_state_for_guild_dispatch_test(),
|
|
GuildState = #{<<"id">> => <<"123">>, <<"unavailable">> => true},
|
|
{noreply, State1} = session_ready:process_guild_state(GuildState, State0),
|
|
Buffer = limited_deque:to_list(maps:get(buffer, State1)),
|
|
?assertEqual(1, length(Buffer)),
|
|
[First | _] = Buffer,
|
|
FirstEvent = maps:get(event, eqwalizer:dynamic_cast(First)),
|
|
?assertEqual(guild_delete, FirstEvent),
|
|
ok.
|
|
|
|
process_guild_state_available_dispatches_guild_create_test() ->
|
|
State0 = base_state_for_guild_dispatch_test(),
|
|
GuildState = #{
|
|
<<"id">> => <<"123">>,
|
|
<<"unavailable">> => false,
|
|
<<"channels">> => [],
|
|
<<"members">> => []
|
|
},
|
|
{noreply, State1} = session_ready:process_guild_state(GuildState, State0),
|
|
Buffer = limited_deque:to_list(maps:get(buffer, State1)),
|
|
?assertEqual(1, length(Buffer)),
|
|
[First | _] = Buffer,
|
|
FirstEvent = maps:get(event, eqwalizer:dynamic_cast(First)),
|
|
?assertEqual(guild_create, FirstEvent),
|
|
ok.
|
|
|
|
process_guild_state_bot_guild_create_marks_guild_available_test() ->
|
|
drain_mailbox(),
|
|
State0 = (base_state_for_guild_dispatch_test())#{bot => true, socket_pid => self()},
|
|
GuildState = #{<<"id">> => <<"123">>, <<"channels">> => [], <<"members">> => []},
|
|
{noreply, _State1} = session_ready:process_guild_state(GuildState, State0),
|
|
receive
|
|
{dispatch, guild_create, GuildCreateData, 1} ->
|
|
?assertEqual(<<"123">>, maps:get(<<"id">>, GuildCreateData)),
|
|
?assertEqual(false, maps:get(<<"unavailable">>, GuildCreateData))
|
|
after 1000 ->
|
|
?assert(false, guild_create_not_dispatched)
|
|
end.
|
|
|
|
process_guild_state_bot_guild_create_after_join_omits_unavailable_test() ->
|
|
drain_mailbox(),
|
|
State0 = (base_state_for_guild_dispatch_test())#{bot => true, socket_pid => self()},
|
|
{noreply, State1} = session:handle_cast({guild_join, 123}, State0),
|
|
GuildState = #{<<"id">> => <<"123">>, <<"channels">> => [], <<"members">> => []},
|
|
{noreply, State2} = session_ready:process_guild_state(GuildState, State1),
|
|
receive
|
|
{dispatch, guild_create, JoinCreateData, 1} ->
|
|
?assertEqual(<<"123">>, maps:get(<<"id">>, JoinCreateData)),
|
|
?assertNot(maps:is_key(<<"unavailable">>, JoinCreateData))
|
|
after 1000 ->
|
|
?assert(false, guild_create_not_dispatched)
|
|
end,
|
|
{noreply, _State3} = session_ready:process_guild_state(GuildState, State2),
|
|
receive
|
|
{dispatch, guild_create, LaterCreateData, 2} ->
|
|
?assertEqual(false, maps:get(<<"unavailable">>, LaterCreateData))
|
|
after 1000 ->
|
|
?assert(false, second_guild_create_not_dispatched)
|
|
end,
|
|
drain_mailbox().
|
|
|
|
process_guild_state_bot_guild_create_after_join_and_outage_omits_unavailable_test() ->
|
|
drain_mailbox(),
|
|
State0 = (base_state_for_guild_dispatch_test())#{bot => true, socket_pid => self()},
|
|
{noreply, State1} = session:handle_cast({guild_join, 123}, State0),
|
|
{noreply, State2} = session_ready:mark_guild_unavailable(123, State1),
|
|
receive
|
|
{dispatch, guild_delete, GuildDeleteData, 1} ->
|
|
?assertEqual(<<"123">>, maps:get(<<"id">>, GuildDeleteData)),
|
|
?assertEqual(true, maps:get(<<"unavailable">>, GuildDeleteData))
|
|
after 1000 ->
|
|
?assert(false, guild_delete_not_dispatched)
|
|
end,
|
|
GuildState = #{<<"id">> => <<"123">>, <<"channels">> => [], <<"members">> => []},
|
|
{noreply, _State3} = session_ready:process_guild_state(GuildState, State2),
|
|
receive
|
|
{dispatch, guild_create, GuildCreateData, 2} ->
|
|
?assertEqual(<<"123">>, maps:get(<<"id">>, GuildCreateData)),
|
|
?assertNot(maps:is_key(<<"unavailable">>, GuildCreateData))
|
|
after 1000 ->
|
|
?assert(false, guild_create_not_dispatched)
|
|
end,
|
|
drain_mailbox().
|
|
|
|
process_guild_state_bot_guild_create_after_join_and_removal_marks_guild_available_test() ->
|
|
drain_mailbox(),
|
|
State0 = (base_state_for_guild_dispatch_test())#{
|
|
bot => true,
|
|
socket_pid => self(),
|
|
user_id => 1
|
|
},
|
|
{noreply, State1} = session:handle_cast({guild_join, 123}, State0),
|
|
{noreply, State2} = session_connection_guild:handle_result_internal(
|
|
123, 3, {error, not_member}, State1
|
|
),
|
|
receive
|
|
{dispatch, guild_delete, GuildDeleteData, 1} ->
|
|
?assertEqual(<<"123">>, maps:get(<<"id">>, GuildDeleteData)),
|
|
?assertNot(maps:is_key(<<"unavailable">>, GuildDeleteData))
|
|
after 1000 ->
|
|
?assert(false, guild_delete_not_dispatched)
|
|
end,
|
|
GuildState = #{<<"id">> => <<"123">>, <<"channels">> => [], <<"members">> => []},
|
|
{noreply, _State3} = session_ready:process_guild_state(GuildState, State2),
|
|
receive
|
|
{dispatch, guild_create, GuildCreateData, 2} ->
|
|
?assertEqual(false, maps:get(<<"unavailable">>, GuildCreateData))
|
|
after 1000 ->
|
|
?assert(false, guild_create_not_dispatched)
|
|
end,
|
|
drain_mailbox().
|
|
|
|
relayed_guild_create_to_bot_marks_guild_available_test() ->
|
|
drain_mailbox(),
|
|
State0 = (base_state_for_guild_dispatch_test())#{bot => true, socket_pid => self()},
|
|
GuildState = #{<<"id">> => <<"123">>, <<"channels">> => [], <<"members">> => []},
|
|
{noreply, _State1} = session:handle_cast({dispatch, guild_create, GuildState}, State0),
|
|
receive
|
|
{dispatch, guild_create, GuildCreateData, 1} ->
|
|
?assertEqual(<<"123">>, maps:get(<<"id">>, GuildCreateData)),
|
|
?assertEqual(false, maps:get(<<"unavailable">>, GuildCreateData))
|
|
after 1000 ->
|
|
?assert(false, guild_create_not_dispatched)
|
|
end.
|
|
|
|
process_guild_state_user_guild_create_omits_unavailable_test() ->
|
|
drain_mailbox(),
|
|
State0 = (base_state_for_guild_dispatch_test())#{bot => false, socket_pid => self()},
|
|
GuildState = #{<<"id">> => <<"123">>, <<"channels">> => [], <<"members">> => []},
|
|
{noreply, _State1} = session_ready:process_guild_state(GuildState, State0),
|
|
receive
|
|
{dispatch, guild_create, GuildCreateData, 1} ->
|
|
?assertEqual(<<"123">>, maps:get(<<"id">>, GuildCreateData)),
|
|
?assertNot(maps:is_key(<<"unavailable">>, GuildCreateData))
|
|
after 1000 ->
|
|
?assert(false, guild_create_not_dispatched)
|
|
end.
|
|
|
|
mark_guild_unavailable_hidden_includes_flag_test() ->
|
|
State0 = #{
|
|
collected_guild_states => [],
|
|
ready => #{<<"guilds">> => []}
|
|
},
|
|
{noreply, State1} = session_ready:mark_guild_unavailable(777, true, State0),
|
|
Collected = maps:get(collected_guild_states, State1, []),
|
|
?assertEqual(1, length(Collected)),
|
|
[UnavailableGuild] = Collected,
|
|
?assertEqual(<<"777">>, maps:get(<<"id">>, UnavailableGuild)),
|
|
?assertEqual(true, maps:get(<<"unavailable">>, UnavailableGuild)),
|
|
?assertEqual(true, maps:get(<<"unavailable_hidden">>, UnavailableGuild)).
|
|
|
|
mark_guild_unavailable_after_ready_dispatches_guild_delete_test() ->
|
|
drain_mailbox(),
|
|
State0 = (base_state_for_guild_dispatch_test())#{
|
|
guilds => #{777 => undefined},
|
|
socket_pid => self()
|
|
},
|
|
{noreply, State1} = session_ready:mark_guild_unavailable(777, true, State0),
|
|
Collected = maps:get(collected_guild_states, State1, []),
|
|
?assertEqual(0, length(Collected)),
|
|
?assertEqual(unavailable, maps:get(777, maps:get(guilds, State1))),
|
|
receive
|
|
{dispatch, guild_delete, GuildDeleteData, _Seq} ->
|
|
?assertEqual(<<"777">>, maps:get(<<"id">>, GuildDeleteData)),
|
|
?assertEqual(true, maps:get(<<"unavailable">>, GuildDeleteData)),
|
|
?assertEqual(true, maps:get(<<"unavailable_hidden">>, GuildDeleteData));
|
|
Other ->
|
|
?assert(false, {unexpected_message, Other})
|
|
after 1000 ->
|
|
?assert(false, guild_delete_not_dispatched)
|
|
end.
|
|
|
|
update_ready_guilds_does_not_duplicate_collected_guild_state_test() ->
|
|
GuildState = #{
|
|
<<"id">> => <<"777">>,
|
|
<<"members">> => [#{<<"user">> => #{<<"id">> => <<"1">>}}]
|
|
},
|
|
Ready = #{<<"guilds">> => [], <<"v">> => 9},
|
|
State = #{ready => Ready, bot => false},
|
|
Updated = session_ready:update_ready_guilds(GuildState, State),
|
|
?assertEqual(Ready, maps:get(ready, Updated)).
|
|
|
|
dispatch_ready_data_bot_unavailable_dispatches_guild_delete_test() ->
|
|
drain_mailbox(),
|
|
UnavailableGuild = #{<<"id">> => <<"987">>, <<"unavailable">> => true},
|
|
State0 = base_ready_state(
|
|
<<"session-ready-test">>,
|
|
42,
|
|
true,
|
|
#{987 => cached_unavailable},
|
|
[UnavailableGuild]
|
|
),
|
|
{noreply, _State1} = session_ready:dispatch_ready_data(State0),
|
|
receive
|
|
{dispatch, ready, ReadyData, ReadySeq} ->
|
|
?assertEqual(1, ReadySeq),
|
|
?assertEqual(
|
|
[#{<<"id">> => <<"987">>, <<"unavailable">> => true}],
|
|
maps:get(<<"guilds">>, ReadyData)
|
|
);
|
|
OtherReady ->
|
|
?assert(false, {unexpected_ready_message, OtherReady})
|
|
after 1000 ->
|
|
?assert(false, ready_not_dispatched)
|
|
end,
|
|
receive
|
|
{dispatch, guild_delete, GuildDeleteData, GuildDeleteSeq} ->
|
|
?assertEqual(2, GuildDeleteSeq),
|
|
?assertEqual(<<"987">>, maps:get(<<"id">>, GuildDeleteData)),
|
|
?assertEqual(true, maps:get(<<"unavailable">>, GuildDeleteData));
|
|
OtherDelete ->
|
|
?assert(false, {unexpected_guild_event, OtherDelete})
|
|
after 1000 ->
|
|
?assert(false, guild_delete_not_dispatched)
|
|
end,
|
|
receive
|
|
{dispatch, guild_create, _GuildCreateData, _GuildCreateSeq} ->
|
|
?assert(false, unexpected_guild_create_for_unavailable_guild)
|
|
after 100 ->
|
|
ok
|
|
end.
|
|
|
|
dispatch_ready_data_nonbot_includes_unavailable_guild_test() ->
|
|
drain_mailbox(),
|
|
UnavailableGuild = #{<<"id">> => <<"654">>, <<"unavailable">> => true},
|
|
State0 = base_ready_state(
|
|
<<"session-ready-nonbot-test">>,
|
|
43,
|
|
false,
|
|
#{654 => cached_unavailable},
|
|
[UnavailableGuild]
|
|
),
|
|
{noreply, _State1} = session_ready:dispatch_ready_data(State0),
|
|
receive
|
|
{dispatch, ready, ReadyData, _ReadySeq} ->
|
|
ReadyGuilds = maps:get(<<"guilds">>, ReadyData, []),
|
|
?assertEqual(1, length(ReadyGuilds)),
|
|
ReadyGuild = hd(ReadyGuilds),
|
|
?assertEqual(<<"654">>, maps:get(<<"id">>, ReadyGuild)),
|
|
?assertEqual(true, maps:get(<<"unavailable">>, ReadyGuild));
|
|
OtherReady ->
|
|
?assert(false, {unexpected_ready_message, OtherReady})
|
|
after 1000 ->
|
|
?assert(false, ready_not_dispatched)
|
|
end,
|
|
receive
|
|
{dispatch, guild_create, _GuildCreateData, _GuildCreateSeq} ->
|
|
?assert(false, unexpected_guild_create_for_nonbot_ready)
|
|
after 100 ->
|
|
ok
|
|
end,
|
|
receive
|
|
{dispatch, guild_delete, _GuildDeleteData, _GuildDeleteSeq} ->
|
|
?assert(false, unexpected_guild_delete_for_nonbot_ready)
|
|
after 100 ->
|
|
ok
|
|
end.
|
|
|
|
dispatch_ready_data_nonbot_includes_api_guild_ids_as_unavailable_test() ->
|
|
drain_mailbox(),
|
|
State0 = base_ready_state(
|
|
<<"session-ready-pending-test">>,
|
|
44,
|
|
false,
|
|
#{111 => undefined, 222 => cached_unavailable},
|
|
[]
|
|
),
|
|
{noreply, _State1} = session_ready:dispatch_ready_data(State0),
|
|
receive
|
|
{dispatch, ready, ReadyData, _ReadySeq} ->
|
|
ReadyGuilds = maps:get(<<"guilds">>, ReadyData, []),
|
|
?assertEqual(2, length(ReadyGuilds)),
|
|
ReadyGuildIds = lists:sort([maps:get(<<"id">>, G) || G <- ReadyGuilds]),
|
|
?assertEqual([<<"111">>, <<"222">>], ReadyGuildIds),
|
|
lists:foreach(
|
|
fun(ReadyGuild) ->
|
|
?assertEqual(true, maps:get(<<"unavailable">>, ReadyGuild))
|
|
end,
|
|
ReadyGuilds
|
|
);
|
|
OtherReady ->
|
|
?assert(false, {unexpected_ready_message, OtherReady})
|
|
after 1000 ->
|
|
?assert(false, ready_not_dispatched)
|
|
end.
|
|
|
|
dispatch_ready_data_nonbot_replaces_unavailable_placeholder_with_collected_guild_test() ->
|
|
drain_mailbox(),
|
|
CollectedGuild = #{
|
|
<<"id">> => <<"111">>,
|
|
<<"name">> => <<"guild-111">>,
|
|
<<"members">> => [#{<<"user">> => #{<<"id">> => <<"1">>}}],
|
|
<<"channels">> => []
|
|
},
|
|
State0 = base_ready_state(
|
|
<<"session-ready-replace-test">>,
|
|
46,
|
|
false,
|
|
#{111 => undefined, 222 => undefined},
|
|
[CollectedGuild]
|
|
),
|
|
{noreply, _State1} = session_ready:dispatch_ready_data(State0),
|
|
receive
|
|
{dispatch, ready, ReadyData, _ReadySeq} ->
|
|
ReadyGuilds = maps:get(<<"guilds">>, ReadyData, []),
|
|
?assertEqual(2, length(ReadyGuilds)),
|
|
FullGuild = find_ready_guild(<<"111">>, ReadyGuilds),
|
|
PlaceholderGuild = find_ready_guild(<<"222">>, ReadyGuilds),
|
|
?assertEqual(<<"guild-111">>, maps:get(<<"name">>, FullGuild)),
|
|
?assertEqual(false, maps:is_key(<<"unavailable">>, FullGuild)),
|
|
?assertEqual(true, maps:get(<<"unavailable">>, PlaceholderGuild));
|
|
OtherReady ->
|
|
?assert(false, {unexpected_ready_message, OtherReady})
|
|
after 1000 ->
|
|
?assert(false, ready_not_dispatched)
|
|
end.
|
|
|
|
dispatch_ready_data_bot_lists_every_guild_as_unavailable_test() ->
|
|
drain_mailbox(),
|
|
State0 = base_ready_state(
|
|
<<"session-ready-bot-placeholder-test">>,
|
|
47,
|
|
true,
|
|
#{
|
|
111 => undefined,
|
|
222 => cached_unavailable,
|
|
333 => unavailable,
|
|
444 => {self(), make_ref()}
|
|
},
|
|
[]
|
|
),
|
|
{noreply, _State1} = session_ready:dispatch_ready_data(State0),
|
|
receive
|
|
{dispatch, ready, ReadyData, ReadySeq} ->
|
|
?assertEqual(1, ReadySeq),
|
|
?assertEqual(
|
|
[
|
|
#{<<"id">> => <<"111">>, <<"unavailable">> => true},
|
|
#{<<"id">> => <<"222">>, <<"unavailable">> => true},
|
|
#{<<"id">> => <<"333">>, <<"unavailable">> => true},
|
|
#{<<"id">> => <<"444">>, <<"unavailable">> => true}
|
|
],
|
|
lists:sort(maps:get(<<"guilds">>, ReadyData))
|
|
);
|
|
OtherReady ->
|
|
?assert(false, {unexpected_ready_message, OtherReady})
|
|
after 1000 ->
|
|
?assert(false, ready_not_dispatched)
|
|
end,
|
|
receive
|
|
{dispatch, guild_create, _GuildCreateData, _GuildCreateSeq} ->
|
|
?assert(false, unexpected_guild_create_for_bot_ready);
|
|
{dispatch, guild_delete, _GuildDeleteData, _GuildDeleteSeq} ->
|
|
?assert(false, unexpected_guild_delete_for_bot_ready)
|
|
after 100 ->
|
|
ok
|
|
end.
|
|
|
|
dispatch_ready_data_bot_lists_collected_guild_as_unavailable_stub_test() ->
|
|
drain_mailbox(),
|
|
CollectedGuild = #{
|
|
<<"id">> => <<"111">>,
|
|
<<"name">> => <<"guild-111">>,
|
|
<<"members">> => [
|
|
#{<<"user">> => #{<<"id">> => <<"1">>, <<"username">> => <<"member-1">>}}
|
|
],
|
|
<<"channels">> => []
|
|
},
|
|
State0 = base_ready_state(
|
|
<<"session-ready-bot-collected-test">>,
|
|
50,
|
|
true,
|
|
#{111 => undefined, 222 => undefined},
|
|
[CollectedGuild]
|
|
),
|
|
{noreply, _State1} = session_ready:dispatch_ready_data(State0),
|
|
receive
|
|
{dispatch, ready, ReadyData, ReadySeq} ->
|
|
?assertEqual(1, ReadySeq),
|
|
?assertEqual(
|
|
[
|
|
#{<<"id">> => <<"111">>, <<"unavailable">> => true},
|
|
#{<<"id">> => <<"222">>, <<"unavailable">> => true}
|
|
],
|
|
lists:sort(maps:get(<<"guilds">>, ReadyData))
|
|
);
|
|
OtherReady ->
|
|
?assert(false, {unexpected_ready_message, OtherReady})
|
|
after 1000 ->
|
|
?assert(false, ready_not_dispatched)
|
|
end,
|
|
receive
|
|
{dispatch, guild_create, GuildCreateData, GuildCreateSeq} ->
|
|
?assertEqual(2, GuildCreateSeq),
|
|
?assertEqual(<<"111">>, maps:get(<<"id">>, GuildCreateData)),
|
|
?assertEqual(<<"guild-111">>, maps:get(<<"name">>, GuildCreateData)),
|
|
?assertEqual(false, maps:get(<<"unavailable">>, GuildCreateData)),
|
|
?assertEqual(
|
|
[#{<<"user">> => #{<<"id">> => <<"1">>}}],
|
|
maps:get(<<"members">>, GuildCreateData)
|
|
);
|
|
OtherGuild ->
|
|
?assert(false, {unexpected_guild_event, OtherGuild})
|
|
after 1000 ->
|
|
?assert(false, guild_create_not_dispatched)
|
|
end,
|
|
receive
|
|
{dispatch, OtherEvent, _OtherData, _OtherSeq} ->
|
|
?assert(false, {unexpected_dispatch_after_burst, OtherEvent})
|
|
after 100 ->
|
|
ok
|
|
end.
|
|
|
|
dispatch_ready_data_bot_marks_guilds_joined_before_ready_available_test() ->
|
|
drain_mailbox(),
|
|
CollectedGuild = #{<<"id">> => <<"111">>, <<"channels">> => [], <<"members">> => []},
|
|
UnavailableGuild = #{<<"id">> => <<"222">>, <<"unavailable">> => true},
|
|
State0 = base_ready_state(
|
|
<<"session-ready-bot-join-test">>,
|
|
51,
|
|
true,
|
|
#{111 => {self(), make_ref()}, 222 => unavailable},
|
|
[UnavailableGuild, CollectedGuild]
|
|
),
|
|
{noreply, State1} = session:handle_cast({guild_join, 111}, State0),
|
|
{noreply, State2} = session:handle_cast({guild_join, 222}, State1),
|
|
drain_mailbox(),
|
|
{noreply, State3} = session_ready:dispatch_ready_data(State2),
|
|
receive
|
|
{dispatch, ready, ReadyData, 1} ->
|
|
?assertEqual(
|
|
[
|
|
#{<<"id">> => <<"111">>, <<"unavailable">> => true},
|
|
#{<<"id">> => <<"222">>, <<"unavailable">> => true}
|
|
],
|
|
lists:sort(maps:get(<<"guilds">>, ReadyData))
|
|
)
|
|
after 1000 ->
|
|
?assert(false, ready_not_dispatched)
|
|
end,
|
|
receive
|
|
{dispatch, guild_create, BurstCreateData, 2} ->
|
|
?assertEqual(<<"111">>, maps:get(<<"id">>, BurstCreateData)),
|
|
?assertEqual(false, maps:get(<<"unavailable">>, BurstCreateData))
|
|
after 1000 ->
|
|
?assert(false, guild_create_not_dispatched)
|
|
end,
|
|
receive
|
|
{dispatch, guild_delete, BurstDeleteData, 3} ->
|
|
?assertEqual(<<"222">>, maps:get(<<"id">>, BurstDeleteData)),
|
|
?assertEqual(true, maps:get(<<"unavailable">>, BurstDeleteData))
|
|
after 1000 ->
|
|
?assert(false, guild_delete_not_dispatched)
|
|
end,
|
|
RecoveredGuild = #{<<"id">> => <<"222">>, <<"channels">> => [], <<"members">> => []},
|
|
{noreply, _State4} = session_ready:process_guild_state(RecoveredGuild, State3),
|
|
receive
|
|
{dispatch, guild_create, RecoveredCreateData, 4} ->
|
|
?assertEqual(<<"222">>, maps:get(<<"id">>, RecoveredCreateData)),
|
|
?assertEqual(false, maps:get(<<"unavailable">>, RecoveredCreateData))
|
|
after 1000 ->
|
|
?assert(false, recovered_guild_create_not_dispatched)
|
|
end,
|
|
drain_mailbox().
|
|
|
|
dispatch_ready_data_includes_shard_metadata_test() ->
|
|
drain_mailbox(),
|
|
State0 = (base_ready_state(
|
|
<<"session-ready-shard-test">>,
|
|
45,
|
|
true,
|
|
#{},
|
|
[]
|
|
))#{
|
|
shard => {1, 4}
|
|
},
|
|
{noreply, _State1} = session_ready:dispatch_ready_data(State0),
|
|
receive
|
|
{dispatch, ready, ReadyData, _ReadySeq} ->
|
|
?assertEqual([1, 4], maps:get(<<"shard">>, ReadyData));
|
|
OtherReady ->
|
|
?assert(false, {unexpected_ready_message, OtherReady})
|
|
after 1000 ->
|
|
?assert(false, ready_not_dispatched)
|
|
end.
|
|
|
|
dispatch_ready_data_includes_gateway_timings_test() ->
|
|
drain_mailbox(),
|
|
GwTimings = gateway_timings:record(
|
|
test_gateway_step, gateway_timings:start() - 10, gateway_timings:new()
|
|
),
|
|
State0 = (base_ready_state(
|
|
<<"session-ready-gateway-timings-test">>,
|
|
48,
|
|
false,
|
|
#{},
|
|
[]
|
|
))#{
|
|
gw_timings => GwTimings,
|
|
is_staff => true
|
|
},
|
|
{noreply, _State1} = session_ready:dispatch_ready_data(State0),
|
|
receive
|
|
{dispatch, ready, ReadyData, _ReadySeq} ->
|
|
Timings = maps:get(<<"_timings_gw">>, ReadyData),
|
|
?assertEqual(<<"microseconds">>, maps:get(<<"unit">>, Timings)),
|
|
?assert(is_binary(maps:get(<<"pod_name">>, Timings))),
|
|
?assertNot(maps:is_key(<<"node_name">>, Timings)),
|
|
?assertNot(maps:is_key(<<"erlang_node_name">>, Timings)),
|
|
Trace = maps:get(<<"trace">>, Timings),
|
|
TraceNames = [maps:get(<<"name">>, Span) || Span <- Trace],
|
|
?assert(lists:member(<<"test_gateway_step">>, TraceNames)),
|
|
ReadySpan = find_trace_span(
|
|
<<"session_ready_dispatch:dispatch_ready_to_socket/1">>, Trace
|
|
),
|
|
ReadyChildren = maps:get(<<"children">>, ReadySpan),
|
|
ReadyChildNames = [maps:get(<<"name">>, Span) || Span <- ReadyChildren],
|
|
?assert(
|
|
lists:member(
|
|
<<"session_ready_collect:collect_ready_presences/2">>, ReadyChildNames
|
|
)
|
|
),
|
|
?assert(
|
|
lists:member(
|
|
<<"session_ready_collect:collect_ready_users/2">>, ReadyChildNames
|
|
)
|
|
),
|
|
?assert(
|
|
lists:member(
|
|
<<"session_ready_dispatch:build_final_ready_data/9">>, ReadyChildNames
|
|
)
|
|
),
|
|
?assertNot(maps:is_key(<<"role">>, Timings)),
|
|
?assertNot(maps:is_key(<<"steps">>, Timings)),
|
|
?assertNot(maps:is_key(<<"nodes">>, Timings));
|
|
OtherReady ->
|
|
?assert(false, {unexpected_ready_message, OtherReady})
|
|
after 1000 ->
|
|
?assert(false, ready_not_dispatched)
|
|
end.
|
|
|
|
dispatch_ready_data_omits_timings_for_non_staff_test() ->
|
|
drain_mailbox(),
|
|
GwTimings = gateway_timings:record(
|
|
test_gateway_step, gateway_timings:start() - 10, gateway_timings:new()
|
|
),
|
|
ApiTimings = #{
|
|
<<"unit">> => <<"microseconds">>,
|
|
<<"total_us">> => 1234,
|
|
<<"pod_name">> => <<"fluxer-api-abc123">>
|
|
},
|
|
BaseState = base_ready_state(
|
|
<<"session-ready-non-staff-timings-test">>,
|
|
49,
|
|
false,
|
|
#{},
|
|
[]
|
|
),
|
|
State0 = BaseState#{
|
|
gw_timings => GwTimings,
|
|
is_staff => false,
|
|
ready => (maps:get(ready, BaseState))#{<<"_timings">> => ApiTimings}
|
|
},
|
|
{noreply, _State1} = session_ready:dispatch_ready_data(State0),
|
|
receive
|
|
{dispatch, ready, ReadyData, _ReadySeq} ->
|
|
?assertNot(maps:is_key(<<"_timings_gw">>, ReadyData)),
|
|
?assertNot(maps:is_key(<<"_timings">>, ReadyData));
|
|
OtherReady ->
|
|
?assert(false, {unexpected_ready_message, OtherReady})
|
|
after 1000 ->
|
|
?assert(false, ready_not_dispatched)
|
|
end.
|
|
|
|
-spec find_trace_span(binary(), [map()]) -> map().
|
|
find_trace_span(Name, Trace) ->
|
|
case [Span || Span <- Trace, maps:get(<<"name">>, Span, undefined) =:= Name] of
|
|
[Span | _] -> Span;
|
|
[] -> error({trace_span_not_found, Name})
|
|
end.
|
|
|
|
-spec drain_mailbox() -> ok.
|
|
drain_mailbox() ->
|
|
receive
|
|
_Message ->
|
|
drain_mailbox()
|
|
after 0 ->
|
|
ok
|
|
end.
|
|
|
|
-spec base_ready_state(binary(), integer(), boolean(), map(), [map()]) -> map().
|
|
base_ready_state(SessionId, UserId, Bot, Guilds, CollectedGuildStates) ->
|
|
#{
|
|
id => SessionId,
|
|
user_id => UserId,
|
|
version => 1,
|
|
ready => #{<<"v">> => 9, <<"guilds">> => []},
|
|
bot => Bot,
|
|
guilds => Guilds,
|
|
channels => #{},
|
|
relationships => #{},
|
|
collected_guild_states => CollectedGuildStates,
|
|
collected_sessions => [],
|
|
seq => 0,
|
|
buffer => limited_deque:new(4096, 16777216),
|
|
socket_pid => self(),
|
|
ignored_events => #{},
|
|
suppress_presence_updates => false,
|
|
pending_presences => [],
|
|
debounce_reactions => false,
|
|
reaction_buffer => [],
|
|
reaction_buffer_timer => undefined,
|
|
presence_pid => undefined
|
|
}.
|
|
|
|
-spec base_state_for_guild_dispatch_test() -> map().
|
|
base_state_for_guild_dispatch_test() ->
|
|
#{
|
|
guilds => #{},
|
|
ready => undefined,
|
|
seq => 0,
|
|
buffer => limited_deque:new(4096, 16777216),
|
|
socket_pid => undefined,
|
|
ignored_events => #{},
|
|
channels => #{},
|
|
relationships => #{},
|
|
suppress_presence_updates => false,
|
|
pending_presences => [],
|
|
presence_pid => undefined,
|
|
debounce_reactions => false,
|
|
reaction_buffer => [],
|
|
reaction_buffer_timer => undefined,
|
|
collected_guild_states => []
|
|
}.
|
|
|
|
collect_ready_presences_does_not_add_one_to_one_dm_as_global_target_test() ->
|
|
{ok, CachePid} = maybe_start_presence_cache(),
|
|
OnlineDmUser = #{
|
|
<<"status">> => <<"online">>,
|
|
<<"user">> => #{<<"id">> => <<"2">>}
|
|
},
|
|
OnlineStranger = #{
|
|
<<"status">> => <<"online">>,
|
|
<<"user">> => #{<<"id">> => <<"99">>}
|
|
},
|
|
ok = presence_cache:put(2, OnlineDmUser),
|
|
ok = presence_cache:put(99, OnlineStranger),
|
|
_ = sys:get_state(CachePid),
|
|
State = #{
|
|
bot => false,
|
|
user_id => 1,
|
|
ready => #{},
|
|
relationships => #{},
|
|
channels => #{
|
|
100 => #{
|
|
<<"id">> => <<"100">>,
|
|
<<"type">> => 1,
|
|
<<"recipients">> => [#{<<"id">> => <<"2">>, <<"username">> => <<"dm-user">>}]
|
|
}
|
|
}
|
|
},
|
|
Presences = session_ready_collect:collect_ready_presences(State, []),
|
|
PresenceIds = [
|
|
maps:get(<<"id">>, maps:get(<<"user">>, P, #{}), undefined)
|
|
|| P <- Presences
|
|
],
|
|
?assertEqual([], PresenceIds),
|
|
?assertEqual(ok, gen_server:stop(CachePid)).
|
|
|
|
collect_ready_presences_includes_friend_without_dm_recipients_test() ->
|
|
{ok, CachePid} = maybe_start_presence_cache(),
|
|
OnlineFriend = #{
|
|
<<"status">> => <<"online">>,
|
|
<<"user">> => #{<<"id">> => <<"2">>}
|
|
},
|
|
ok = presence_cache:put(2, OnlineFriend),
|
|
_ = sys:get_state(CachePid),
|
|
Ready = #{
|
|
<<"relationships">> => [
|
|
#{<<"id">> => <<"2">>, <<"type">> => 1, <<"user">> => #{<<"id">> => <<"2">>}}
|
|
],
|
|
<<"private_channels">> => [
|
|
#{<<"id">> => <<"100">>, <<"type">> => 1}
|
|
]
|
|
},
|
|
State = #{
|
|
bot => false,
|
|
user_id => 1,
|
|
ready => Ready,
|
|
relationships => session_init:load_relationships(Ready),
|
|
channels => session_init:load_private_channels(Ready)
|
|
},
|
|
Presences = session_ready_collect:collect_ready_presences(State, []),
|
|
PresenceIds = [
|
|
maps:get(<<"id">>, maps:get(<<"user">>, P, #{}), undefined)
|
|
|| P <- Presences
|
|
],
|
|
?assertEqual([<<"2">>], PresenceIds),
|
|
?assertEqual(ok, gen_server:stop(CachePid)).
|
|
|
|
collect_ready_presences_uses_live_friend_presence_when_cache_missing_test() ->
|
|
{ok, CachePid} = maybe_start_presence_cache(),
|
|
{ManagerPid, ManagerStarted} = maybe_start_presence_manager(),
|
|
TargetId = 32,
|
|
{ok, TargetPid} = presence_manager:start_or_lookup(presence_request(TargetId)),
|
|
{ok, _TargetSessions} = gen_server:call(
|
|
TargetPid, {session_connect, presence_connect_req(<<"target-ready-session">>)}, 5000
|
|
),
|
|
_ = sys:get_state(CachePid),
|
|
ok = presence_cache:delete(TargetId),
|
|
_ = sys:get_state(CachePid),
|
|
?assertEqual(not_found, presence_cache:get(TargetId)),
|
|
Ready = #{
|
|
<<"relationships">> => [
|
|
#{
|
|
<<"id">> => integer_to_binary(TargetId),
|
|
<<"type">> => 1,
|
|
<<"user">> => #{<<"id">> => integer_to_binary(TargetId)}
|
|
}
|
|
]
|
|
},
|
|
State = #{
|
|
bot => false,
|
|
user_id => 1,
|
|
ready => Ready,
|
|
relationships => session_init:load_relationships(Ready),
|
|
channels => #{}
|
|
},
|
|
Presences = session_ready_collect:collect_ready_presences(State, []),
|
|
?assertEqual([TargetId], presence_ids(Presences)),
|
|
_ = sys:get_state(CachePid),
|
|
?assertMatch({ok, _}, presence_cache:get(TargetId)),
|
|
ok = gen_server:stop(TargetPid),
|
|
stop_presence_manager(ManagerPid, ManagerStarted),
|
|
?assertEqual(ok, gen_server:stop(CachePid)).
|
|
|
|
collect_ready_presences_skips_offline_dm_recipients_test() ->
|
|
{ok, CachePid} = maybe_start_presence_cache(),
|
|
State = #{
|
|
bot => false,
|
|
user_id => 1,
|
|
ready => #{},
|
|
relationships => #{},
|
|
channels => #{
|
|
100 => #{
|
|
<<"id">> => <<"100">>,
|
|
<<"type">> => 1,
|
|
<<"recipients">> => [#{<<"id">> => <<"5">>, <<"username">> => <<"dm-user">>}]
|
|
}
|
|
}
|
|
},
|
|
?assertEqual([], session_ready_collect:collect_ready_presences(State, [])),
|
|
?assertEqual(ok, gen_server:stop(CachePid)).
|
|
|
|
maybe_start_presence_cache() ->
|
|
case whereis(presence_cache) of
|
|
undefined -> presence_cache:start_link();
|
|
Existing when is_pid(Existing) -> {ok, Existing}
|
|
end.
|
|
|
|
maybe_start_presence_manager() ->
|
|
process_registry:init(),
|
|
case whereis(presence_manager) of
|
|
undefined ->
|
|
case presence_manager:start_link() of
|
|
{ok, Pid} -> {Pid, true};
|
|
{error, {already_started, Pid}} -> {Pid, false}
|
|
end;
|
|
Existing when is_pid(Existing) ->
|
|
{Existing, false}
|
|
end.
|
|
|
|
stop_presence_manager(_Pid, false) ->
|
|
ok;
|
|
stop_presence_manager(Pid, true) ->
|
|
try gen_server:stop(Pid) of
|
|
ok -> ok
|
|
catch
|
|
error:_ -> ok;
|
|
exit:_ -> ok
|
|
end.
|
|
|
|
presence_request(UserId) ->
|
|
#{
|
|
user_id => UserId,
|
|
user_data => #{
|
|
<<"id">> => integer_to_binary(UserId),
|
|
<<"username">> => <<"ready-target">>,
|
|
<<"discriminator">> => <<"0001">>,
|
|
<<"avatar">> => null,
|
|
<<"flags">> => 0
|
|
},
|
|
guild_ids => [],
|
|
friend_ids => [],
|
|
group_dm_recipients => #{},
|
|
status => online,
|
|
custom_status => null
|
|
}.
|
|
|
|
presence_connect_req(SessionId) ->
|
|
#{
|
|
session_id => SessionId,
|
|
status => online,
|
|
afk => false,
|
|
mobile => false,
|
|
socket_pid => undefined
|
|
}.
|
|
|
|
presence_ids(Presences) ->
|
|
[
|
|
snowflake_id:parse_maybe(maps:get(<<"id">>, maps:get(<<"user">>, P, #{}), undefined))
|
|
|| P <- Presences
|
|
].
|
|
|
|
-spec find_ready_guild(binary(), [map()]) -> map().
|
|
find_ready_guild(GuildId, ReadyGuilds) ->
|
|
case [G || G <- ReadyGuilds, maps:get(<<"id">>, G, undefined) =:= GuildId] of
|
|
[Guild] -> Guild;
|
|
[] -> error({missing_ready_guild, GuildId})
|
|
end.
|