mirror of
https://github.com/fluxerapp/fluxer
synced 2026-10-07 19:22:14 +09:00
243 lines
8.6 KiB
Erlang
243 lines
8.6 KiB
Erlang
%% SPDX-License-Identifier: AGPL-3.0-or-later
|
|
|
|
-module(session_manager_shard_lookup_tests).
|
|
-typing([eqwalizer]).
|
|
|
|
-include_lib("eunit/include/eunit.hrl").
|
|
|
|
normalize_status_test() ->
|
|
?assertEqual(undefined, session_manager_shard_lookup:normalize_status(undefined)),
|
|
?assertEqual(undefined, session_manager_shard_lookup:normalize_status(null)),
|
|
?assertEqual(<<"online">>, session_manager_shard_lookup:normalize_status(<<"online">>)),
|
|
?assertEqual(<<"idle">>, session_manager_shard_lookup:normalize_status(<<"idle">>)),
|
|
?assertEqual(
|
|
<<"invisible">>, session_manager_shard_lookup:normalize_status(<<"invisible">>)
|
|
),
|
|
?assertEqual(undefined, session_manager_shard_lookup:normalize_status(123)),
|
|
ok.
|
|
|
|
select_initial_status_test() ->
|
|
?assertEqual(
|
|
<<"idle">>, session_manager_shard_lookup:select_initial_status(undefined, <<"idle">>)
|
|
),
|
|
?assertEqual(
|
|
<<"dnd">>, session_manager_shard_lookup:select_initial_status(<<"unknown">>, <<"dnd">>)
|
|
),
|
|
?assertEqual(
|
|
<<"idle">>, session_manager_shard_lookup:select_initial_status(<<"online">>, <<"idle">>)
|
|
),
|
|
?assertEqual(
|
|
<<"online">>,
|
|
session_manager_shard_lookup:select_initial_status(<<"online">>, <<"online">>)
|
|
),
|
|
?assertEqual(
|
|
<<"dnd">>, session_manager_shard_lookup:select_initial_status(<<"dnd">>, <<"online">>)
|
|
),
|
|
ok.
|
|
|
|
parse_presence_preserves_invisible_stored_status_test() ->
|
|
Data = #{<<"user_settings">> => #{<<"status">> => <<"invisible">>}},
|
|
IdentifyData = #{presence => #{<<"status">> => <<"online">>}},
|
|
?assertEqual(invisible, session_manager_shard_lookup:parse_presence(Data, IdentifyData)).
|
|
|
|
parse_presence_preserves_invisible_identify_status_test() ->
|
|
Data = #{<<"user_settings">> => #{<<"status">> => <<"online">>}},
|
|
IdentifyData = #{presence => #{<<"status">> => <<"invisible">>}},
|
|
?assertEqual(invisible, session_manager_shard_lookup:parse_presence(Data, IdentifyData)).
|
|
|
|
normalize_coordinate_test() ->
|
|
?assertEqual(undefined, session_manager_shard_lookup:normalize_coordinate(undefined)),
|
|
?assertEqual(undefined, session_manager_shard_lookup:normalize_coordinate(null)),
|
|
?assertEqual(1.5, session_manager_shard_lookup:normalize_coordinate(1.5)),
|
|
?assertEqual(<<"test">>, session_manager_shard_lookup:normalize_coordinate(<<"test">>)),
|
|
ok.
|
|
|
|
add_coordinates_test() ->
|
|
Base = #{<<"type">> => <<"session">>},
|
|
?assertEqual(
|
|
Base, session_manager_shard_lookup:add_coordinates(Base, undefined, undefined)
|
|
),
|
|
?assertEqual(
|
|
#{<<"type">> => <<"session">>, <<"latitude">> => 1.0},
|
|
session_manager_shard_lookup:add_coordinates(Base, 1.0, undefined)
|
|
),
|
|
?assertEqual(
|
|
#{<<"type">> => <<"session">>, <<"longitude">> => 2.0},
|
|
session_manager_shard_lookup:add_coordinates(Base, undefined, 2.0)
|
|
),
|
|
?assertEqual(
|
|
#{<<"type">> => <<"session">>, <<"latitude">> => 1.0, <<"longitude">> => 2.0},
|
|
session_manager_shard_lookup:add_coordinates(Base, 1.0, 2.0)
|
|
),
|
|
ok.
|
|
|
|
validate_transfer_token_test() ->
|
|
GoodToken = <<"good_token">>,
|
|
GoodHash = utils:hash_token(GoodToken),
|
|
?assertEqual(
|
|
ok,
|
|
session_manager_shard_lookup:validate_transfer_token(GoodToken, #{
|
|
token_hash => GoodHash
|
|
})
|
|
),
|
|
?assertEqual(
|
|
{error, invalid_token},
|
|
session_manager_shard_lookup:validate_transfer_token(<<"bad_token">>, #{
|
|
token_hash => GoodHash
|
|
})
|
|
),
|
|
?assertEqual(
|
|
{error, invalid_token},
|
|
session_manager_shard_lookup:validate_transfer_token(GoodToken, #{})
|
|
).
|
|
|
|
lookup_or_rehydrate_restores_state_on_invalid_token_test() ->
|
|
with_session_state_transfer(fun() ->
|
|
SessionId = <<"session-rehydrate-invalid-token">>,
|
|
TransferState = #{token_hash => utils:hash_token(<<"expected">>)},
|
|
ok = session_state_transfer:push_state(node(), SessionId, TransferState),
|
|
{Reply, _StateAfter} = session_manager_shard_lookup:lookup_or_rehydrate(
|
|
SessionId, <<"unexpected">>, self(), new_test_state()
|
|
),
|
|
?assertEqual({error, invalid_token}, Reply),
|
|
?assertEqual({ok, TransferState}, session_state_transfer:pop_state(SessionId))
|
|
end).
|
|
|
|
lookup_remote_on_nodes_empty_test() ->
|
|
?assertEqual(
|
|
{error, not_found},
|
|
session_manager_shard_lookup:lookup_remote_on_nodes([], {session, <<"missing">>})
|
|
).
|
|
|
|
select_remote_session_pid_ignores_not_found_entries_test() ->
|
|
Pid = spawn(fun() ->
|
|
receive
|
|
stop -> ok
|
|
after infinity ->
|
|
ok
|
|
end
|
|
end),
|
|
try
|
|
?assertEqual(
|
|
{ok, Pid},
|
|
session_manager_shard_lookup:select_remote_session_pid(
|
|
[undefined, {badrpc, nodedown}, Pid]
|
|
)
|
|
)
|
|
after
|
|
Pid ! stop
|
|
end.
|
|
|
|
select_remote_session_pid_returns_not_found_without_pid_test() ->
|
|
?assertEqual(
|
|
{error, not_found},
|
|
session_manager_shard_lookup:select_remote_session_pid(
|
|
[undefined, {badrpc, nodedown}, {error, not_found}]
|
|
)
|
|
).
|
|
|
|
remote_lookup_nodes_excludes_local_node_test() ->
|
|
?assertEqual([], session_manager_shard_lookup:remote_lookup_nodes(<<"session">>, [node()])).
|
|
|
|
remote_lookup_nodes_prioritizes_hash_owner_test() ->
|
|
RemoteA = 'gateway_a@example',
|
|
RemoteB = 'gateway_b@example',
|
|
Nodes = lists:usort([node(), RemoteA, RemoteB]),
|
|
SessionId = session_id_with_remote_owner(Nodes, 0),
|
|
OwnerNode = gateway_node_router:select_owner_node(SessionId, Nodes),
|
|
LookupNodes = session_manager_shard_lookup:remote_lookup_nodes(SessionId, Nodes),
|
|
?assert(OwnerNode =/= node()),
|
|
?assertEqual(OwnerNode, hd(LookupNodes)),
|
|
?assertNot(lists:member(node(), LookupNodes)),
|
|
?assertEqual(lists:sort([RemoteA, RemoteB]), lists:sort(LookupNodes)).
|
|
|
|
get_presence_custom_status_test() ->
|
|
?assertEqual(null, session_manager_shard_lookup:get_presence_custom_status(null)),
|
|
?assertEqual(null, session_manager_shard_lookup:get_presence_custom_status(#{})),
|
|
?assertEqual(
|
|
#{<<"text">> => <<"hello">>},
|
|
session_manager_shard_lookup:get_presence_custom_status(
|
|
#{<<"custom_status">> => #{<<"text">> => <<"hello">>}}
|
|
)
|
|
),
|
|
?assertEqual(null, session_manager_shard_lookup:get_presence_custom_status(not_a_map)),
|
|
ok.
|
|
|
|
parse_guild_ids_extracts_from_guilds_array_test() ->
|
|
Data = #{
|
|
<<"guilds">> => [
|
|
#{<<"id">> => <<"100">>},
|
|
#{<<"id">> => <<"200">>},
|
|
#{<<"id">> => <<"300">>}
|
|
]
|
|
},
|
|
?assertEqual([100, 200, 300], session_manager_shard_lookup:parse_guild_ids(Data)).
|
|
|
|
parse_guild_ids_returns_empty_when_no_guilds_test() ->
|
|
?assertEqual([], session_manager_shard_lookup:parse_guild_ids(#{})),
|
|
?assertEqual([], session_manager_shard_lookup:parse_guild_ids(#{<<"guilds">> => []})).
|
|
|
|
parse_guild_ids_skips_non_map_entries_test() ->
|
|
Data = #{
|
|
<<"guilds">> => [
|
|
#{<<"id">> => <<"100">>},
|
|
<<"not_a_map">>,
|
|
#{<<"id">> => <<"200">>}
|
|
]
|
|
},
|
|
?assertEqual([100, 200], session_manager_shard_lookup:parse_guild_ids(Data)).
|
|
|
|
parse_guild_ids_skips_entries_without_id_test() ->
|
|
Data = #{
|
|
<<"guilds">> => [
|
|
#{<<"id">> => <<"100">>},
|
|
#{<<"name">> => <<"no_id">>},
|
|
#{<<"id">> => <<"200">>}
|
|
]
|
|
},
|
|
?assertEqual([100, 200], session_manager_shard_lookup:parse_guild_ids(Data)).
|
|
|
|
parse_guild_ids_handles_unavailable_guilds_test() ->
|
|
Data = #{
|
|
<<"guilds">> => [
|
|
#{<<"id">> => <<"100">>, <<"unavailable">> => true},
|
|
#{<<"id">> => <<"200">>}
|
|
]
|
|
},
|
|
?assertEqual([100, 200], session_manager_shard_lookup:parse_guild_ids(Data)).
|
|
|
|
new_test_state() ->
|
|
#{
|
|
sessions => #{},
|
|
identify_attempts => [],
|
|
pending_identifies => #{},
|
|
identify_workers => #{},
|
|
shard_index => 0
|
|
}.
|
|
|
|
session_id_with_remote_owner(Nodes, Index) ->
|
|
SessionId = <<"session-remote-owner-", (integer_to_binary(Index))/binary>>,
|
|
case gateway_node_router:select_owner_node(SessionId, Nodes) =/= node() of
|
|
true -> SessionId;
|
|
false when Index < 1000 -> session_id_with_remote_owner(Nodes, Index + 1);
|
|
false -> error(no_remote_owner_session_id)
|
|
end.
|
|
|
|
with_session_state_transfer(Fun) ->
|
|
process_registry:init(),
|
|
case whereis(session_state_transfer) of
|
|
undefined ->
|
|
{ok, Pid} = session_state_transfer:start_link(),
|
|
try
|
|
Fun()
|
|
after
|
|
try
|
|
gen_server:stop(Pid)
|
|
catch
|
|
_:_ -> ok
|
|
end
|
|
end;
|
|
_Pid ->
|
|
Fun()
|
|
end.
|