mirror of
https://github.com/fluxerapp/fluxer
synced 2026-10-07 19:22:14 +09:00
546 lines
21 KiB
Erlang
546 lines
21 KiB
Erlang
%% SPDX-License-Identifier: AGPL-3.0-or-later
|
|
|
|
-module(gateway_handler_tests).
|
|
-typing([eqwalizer]).
|
|
-include_lib("eunit/include/eunit.hrl").
|
|
|
|
-define(TEST_GATEWAY_RATE_LIMIT_MAX_EVENTS, 600).
|
|
-define(TEST_IDENTIFY_MAX_PER_IP, 300).
|
|
|
|
websocket_info_session_reconnect_sends_reconnect_then_close_test() ->
|
|
assert_reconnect_close(
|
|
gateway_handler:websocket_info(session_reconnect, new_json_state()),
|
|
<<"Session drain requested; reconnect to continue">>
|
|
).
|
|
|
|
start_session_with_drain_guard_holds_during_drain_test() ->
|
|
Request = #{},
|
|
assert_pending_identify(
|
|
gateway_handler_identify:start_session_with_drain_guard(
|
|
true, Request, self(), new_json_state()
|
|
),
|
|
Request,
|
|
self()
|
|
).
|
|
|
|
handle_session_start_result_holds_draining_without_context_test() ->
|
|
assert_held_without_pending(
|
|
gateway_handler_identify:handle_session_start_result(
|
|
{error, draining}, new_json_state()
|
|
)
|
|
).
|
|
|
|
handle_session_start_result_holds_not_eligible_without_context_test() ->
|
|
assert_held_without_pending(
|
|
gateway_handler_identify:handle_session_start_result(
|
|
{error, not_eligible}, new_json_state()
|
|
)
|
|
).
|
|
|
|
start_session_zero_rollout_holds_socket_test() ->
|
|
OldConfig = gateway_rollout_config:get(),
|
|
try
|
|
persistent_term:put(gateway_rollout_config, OldConfig#{
|
|
<<"session_rollout_percentage">> => 0
|
|
}),
|
|
Request = #{},
|
|
assert_pending_identify(
|
|
gateway_handler_identify:start_session(Request, self(), new_json_state()),
|
|
Request,
|
|
self()
|
|
)
|
|
after
|
|
persistent_term:put(gateway_rollout_config, OldConfig)
|
|
end.
|
|
|
|
handle_session_start_result_retries_exhausted_holds_without_context_test() ->
|
|
State = (gateway_handler:new_state())#{
|
|
version => 1, encoding => json, compress_ctx => gateway_compress:new_context(none)
|
|
},
|
|
assert_held_without_pending(
|
|
gateway_handler_identify:handle_session_start_result(
|
|
{error, {retries_exhausted, timeout}}, State
|
|
)
|
|
).
|
|
|
|
handle_session_start_result_retries_exhausted_5xx_holds_without_context_test() ->
|
|
State = (gateway_handler:new_state())#{
|
|
version => 1, encoding => json, compress_ctx => gateway_compress:new_context(none)
|
|
},
|
|
assert_held_without_pending(
|
|
gateway_handler_identify:handle_session_start_result(
|
|
{error, {retries_exhausted, {rpc_error, 503, <<"Service unavailable">>}}}, State
|
|
)
|
|
).
|
|
|
|
handle_session_start_result_server_error_closes_with_unknown_error_test() ->
|
|
State = (gateway_handler:new_state())#{
|
|
version => 1, encoding => json, compress_ctx => gateway_compress:new_context(none)
|
|
},
|
|
{[{close, CloseCode, _Reason}], _NewState} =
|
|
gateway_handler_identify:handle_session_start_result(
|
|
{error, {server_error, 500}}, State
|
|
),
|
|
?assertEqual(constants:close_code_to_num(unknown_error), CloseCode).
|
|
|
|
websocket_init_schedules_tokened_heartbeat_timer_test() ->
|
|
{[{text, _Frame}], State} = gateway_handler:websocket_init(new_json_state()),
|
|
?assertMatch({_TimerRef, _Token}, maps:get(heartbeat_timer, State)),
|
|
gateway_handler_heartbeat:cancel_heartbeat_timer(State).
|
|
|
|
heartbeat_check_ignores_stale_token_test() ->
|
|
State0 = heartbeat_test_state(),
|
|
State1 = gateway_handler_heartbeat:schedule_heartbeat_check(State0),
|
|
{TimerRef, Token} = maps:get(heartbeat_timer, State1),
|
|
{ok, State2} = gateway_handler:websocket_info({heartbeat_check, make_ref()}, State1),
|
|
?assertEqual({TimerRef, Token}, maps:get(heartbeat_timer, State2)),
|
|
gateway_handler_heartbeat:cancel_heartbeat_timer(State2).
|
|
|
|
heartbeat_check_replaces_matched_timer_test() ->
|
|
State0 = heartbeat_test_state(),
|
|
State1 = gateway_handler_heartbeat:schedule_heartbeat_check(State0),
|
|
{TimerRef, Token} = maps:get(heartbeat_timer, State1),
|
|
{ok, State2} = gateway_handler:websocket_info({heartbeat_check, Token}, State1),
|
|
?assertMatch({_NewTimerRef, _NewToken}, maps:get(heartbeat_timer, State2)),
|
|
?assertNotEqual({TimerRef, Token}, maps:get(heartbeat_timer, State2)),
|
|
gateway_handler_heartbeat:cancel_heartbeat_timer(State2).
|
|
|
|
legacy_heartbeat_check_ignored_when_tokened_timer_active_test() ->
|
|
State0 = heartbeat_test_state(),
|
|
State1 = gateway_handler_heartbeat:schedule_heartbeat_check(State0),
|
|
{TimerRef, Token} = maps:get(heartbeat_timer, State1),
|
|
{ok, State2} = gateway_handler:websocket_info({heartbeat_check}, State1),
|
|
?assertEqual({TimerRef, Token}, maps:get(heartbeat_timer, State2)),
|
|
gateway_handler_heartbeat:cancel_heartbeat_timer(State2).
|
|
|
|
pending_identify_retry_ignores_stale_token_test() ->
|
|
Timer = erlang:send_after(60000, self(), stale_pending_identify_retry),
|
|
CurrentToken = make_ref(),
|
|
State0 = (new_json_state())#{
|
|
pending_identify => {#{token => <<"t">>}, self()},
|
|
pending_identify_retry_timer => {Timer, CurrentToken}
|
|
},
|
|
{ok, State1} = gateway_handler:websocket_info(
|
|
{retry_pending_identify, make_ref()}, State0
|
|
),
|
|
?assertEqual(State0, State1),
|
|
cancel_pending_identify_timer(State1).
|
|
|
|
legacy_pending_identify_retry_ignored_when_tokened_timer_active_test() ->
|
|
Timer = erlang:send_after(60000, self(), stale_pending_identify_retry),
|
|
CurrentToken = make_ref(),
|
|
State0 = (new_json_state())#{
|
|
pending_identify => {#{token => <<"t">>}, self()},
|
|
pending_identify_retry_timer => {Timer, CurrentToken}
|
|
},
|
|
{ok, State1} = gateway_handler:websocket_info(retry_pending_identify, State0),
|
|
?assertEqual(State0, State1),
|
|
cancel_pending_identify_timer(State1).
|
|
|
|
parse_forwarded_for_ipv4_test() ->
|
|
?assertEqual(<<"203.0.113.7">>, gateway_handler:parse_forwarded_for(<<"203.0.113.7">>)).
|
|
parse_forwarded_for_ipv4_with_port_test() ->
|
|
?assertEqual(
|
|
<<"203.0.113.7">>, gateway_handler:parse_forwarded_for(<<"203.0.113.7:8080">>)
|
|
).
|
|
parse_forwarded_for_ipv4_with_port_and_extra_entries_test() ->
|
|
?assertEqual(
|
|
<<"203.0.113.7">>,
|
|
gateway_handler:parse_forwarded_for(<<" 203.0.113.7:8080 , 10.0.0.1">>)
|
|
).
|
|
parse_forwarded_for_ipv6_test() ->
|
|
?assertEqual(<<"2001:db8::1">>, gateway_handler:parse_forwarded_for(<<"2001:db8::1">>)).
|
|
parse_forwarded_for_ipv6_with_brackets_test() ->
|
|
?assertEqual(<<"2001:db8::1">>, gateway_handler:parse_forwarded_for(<<"[2001:db8::1]">>)).
|
|
parse_forwarded_for_ipv6_with_brackets_and_port_test() ->
|
|
?assertEqual(
|
|
<<"2001:db8::1">>, gateway_handler:parse_forwarded_for(<<"[2001:db8::1]:443">>)
|
|
).
|
|
parse_forwarded_for_ipv6_with_spaces_test() ->
|
|
?assertEqual(
|
|
<<"2001:db8::1">>, gateway_handler:parse_forwarded_for(<<" [2001:db8::1] ">>)
|
|
).
|
|
parse_forwarded_for_invalid_ip_test() ->
|
|
?assertEqual(<<>>, gateway_handler:parse_forwarded_for(<<"not_an_ip">>)).
|
|
parse_forwarded_for_invalid_ipv4_octet_test() ->
|
|
?assertEqual(<<>>, gateway_handler:parse_forwarded_for(<<"203.0.113.300">>)).
|
|
parse_forwarded_for_unterminated_bracket_test() ->
|
|
?assertEqual(<<>>, gateway_handler:parse_forwarded_for(<<"[2001:db8::1">>)).
|
|
parse_version_test() ->
|
|
?assertEqual(1, gateway_handler:parse_version(<<"1">>)),
|
|
?assertEqual(undefined, gateway_handler:parse_version(<<"2">>)),
|
|
?assertEqual(undefined, gateway_handler:parse_version(undefined)).
|
|
|
|
parse_ignored_events_test() ->
|
|
?assertEqual({ok, []}, gateway_handler_identify:parse_ignored_events(undefined)),
|
|
?assertEqual({ok, []}, gateway_handler_identify:parse_ignored_events(null)),
|
|
?assertEqual(
|
|
{ok, [<<"TYPING_START">>]},
|
|
gateway_handler_identify:parse_ignored_events([<<"typing_start">>])
|
|
),
|
|
?assertEqual(
|
|
{error, invalid_ignored_events}, gateway_handler_identify:parse_ignored_events([123])
|
|
),
|
|
?assertEqual(
|
|
{error, invalid_ignored_events},
|
|
gateway_handler_identify:parse_ignored_events(<<"not_a_list">>)
|
|
).
|
|
|
|
adjust_status_test() ->
|
|
?assertEqual(invisible, gateway_handler_dispatch:adjust_status(offline)),
|
|
?assertEqual(online, gateway_handler_dispatch:adjust_status(online)),
|
|
?assertEqual(idle, gateway_handler_dispatch:adjust_status(idle)).
|
|
|
|
check_rate_limit_blocks_general_flood_test() ->
|
|
with_rate_limits_enabled(fun() ->
|
|
Now = erlang:system_time(millisecond),
|
|
Events = lists:duplicate(?TEST_GATEWAY_RATE_LIMIT_MAX_EVENTS, Now - 1000),
|
|
State = (gateway_handler:new_state())#{
|
|
rate_limit_state => #{events => Events, op_events => #{}}
|
|
},
|
|
?assertMatch(
|
|
{rate_limited, _}, gateway_handler_rate_limit:check_rate_limit(State, heartbeat)
|
|
)
|
|
end).
|
|
|
|
check_presence_rate_limit_soft_drops_only_presence_test() ->
|
|
with_rate_limits_enabled(fun() ->
|
|
Now = erlang:system_time(millisecond),
|
|
Events = lists:duplicate(5, Now - 1000),
|
|
State = (gateway_handler:new_state())#{
|
|
rate_limit_state => #{
|
|
events => [], op_events => #{presence_update => Events}
|
|
}
|
|
},
|
|
{opcode_rate_limited, NewState} = gateway_handler_rate_limit:check_rate_limit(
|
|
State, presence_update
|
|
),
|
|
RateLimitState = maps:get(rate_limit_state, NewState),
|
|
?assertEqual(1, length(maps:get(events, RateLimitState)))
|
|
end).
|
|
|
|
enqueue_voice_update_keeps_latest_update_for_connection_test() ->
|
|
Queue0 = queue:new(),
|
|
Data1 = #{
|
|
<<"guild_id">> => <<"1">>, <<"connection_id">> => <<"conn-1">>, <<"self_mute">> => false
|
|
},
|
|
Data2 = Data1#{<<"self_mute">> => true},
|
|
Queue1 = gateway_handler_voice:enqueue_voice_update(Queue0, Data1),
|
|
Queue2 = gateway_handler_voice:enqueue_voice_update(Queue1, Data2),
|
|
?assertEqual([Data2], queue:to_list(Queue2)).
|
|
|
|
enqueue_voice_update_keeps_distinct_connections_test() ->
|
|
Queue0 = queue:new(),
|
|
Data1 = #{
|
|
<<"guild_id">> => <<"1">>, <<"connection_id">> => <<"conn-1">>, <<"self_mute">> => false
|
|
},
|
|
Data2 = Data1#{<<"connection_id">> => <<"conn-2">>, <<"self_mute">> => true},
|
|
Queue1 = gateway_handler_voice:enqueue_voice_update(Queue0, Data1),
|
|
Queue2 = gateway_handler_voice:enqueue_voice_update(Queue1, Data2),
|
|
?assertEqual([Data1, Data2], queue:to_list(Queue2)).
|
|
|
|
handle_request_guild_members_queues_latest_request_while_worker_active_test() ->
|
|
ExistingWorkerPid = self(),
|
|
State = (gateway_handler:new_state())#{request_guild_members_pid => ExistingWorkerPid},
|
|
Data = #{<<"guild_ids">> => [<<"1">>, <<"2">>]},
|
|
?assertMatch(
|
|
{ok, #{request_guild_members_pending := Data}},
|
|
gateway_handler_dispatch:handle_request_guild_members(Data, self(), State)
|
|
).
|
|
|
|
handle_request_guild_counts_drops_when_socket_worker_limit_reached_test() ->
|
|
Ref = make_ref(),
|
|
WorkerPid = self(),
|
|
State = (gateway_handler:new_state())#{
|
|
request_worker_max => 1,
|
|
request_workers => #{
|
|
Ref => #{pid => WorkerPid, type => lazy_request, timer => undefined}
|
|
}
|
|
},
|
|
{ok, NewState} = gateway_handler_dispatch:handle_request_guild_counts(
|
|
#{}, self(), eqwalizer:dynamic_cast(State)
|
|
),
|
|
?assertEqual(1, maps:size(maps:get(request_workers, NewState))).
|
|
|
|
request_worker_down_removes_tracked_worker_test() ->
|
|
Ref = make_ref(),
|
|
WorkerPid = self(),
|
|
State = (gateway_handler:new_state())#{
|
|
request_workers => #{
|
|
Ref => #{pid => WorkerPid, type => lazy_request, timer => undefined}
|
|
}
|
|
},
|
|
{ok, NewState} = gateway_handler_dispatch:handle_request_worker_down(
|
|
Ref, WorkerPid, normal, State
|
|
),
|
|
?assertEqual(#{}, maps:get(request_workers, NewState)).
|
|
|
|
request_worker_timeout_removes_and_kills_worker_test() ->
|
|
WorkerPid = spawn(fun request_worker_wait_loop/0),
|
|
Ref = make_ref(),
|
|
State = (gateway_handler:new_state())#{
|
|
request_workers => #{
|
|
Ref => #{pid => WorkerPid, type => lazy_request, timer => undefined}
|
|
}
|
|
},
|
|
{ok, NewState} = gateway_handler_dispatch:handle_request_worker_timeout(
|
|
Ref, lazy_request, State
|
|
),
|
|
ok = gateway_retry_timer:wait(10),
|
|
?assertEqual(#{}, maps:get(request_workers, NewState)),
|
|
?assertNot(erlang:is_process_alive(WorkerPid)).
|
|
|
|
validate_presence_data_valid_test() ->
|
|
Data = #{<<"status">> => <<"online">>, <<"afk">> => false, <<"mobile">> => false},
|
|
{ok, Result} = gateway_handler_dispatch:validate_presence_data(Data),
|
|
?assertEqual(online, maps:get(status, Result)).
|
|
|
|
validate_presence_data_missing_status_test() ->
|
|
?assertEqual(
|
|
{error, invalid_presence},
|
|
gateway_handler_dispatch:validate_presence_data(#{<<"afk">> => false})
|
|
).
|
|
validate_presence_data_empty_map_test() ->
|
|
?assertEqual(
|
|
{error, invalid_presence}, gateway_handler_dispatch:validate_presence_data(#{})
|
|
).
|
|
validate_presence_data_not_a_map_test() ->
|
|
?assertEqual(
|
|
{error, invalid_presence}, gateway_handler_dispatch:validate_presence_data(not_a_map())
|
|
).
|
|
validate_presence_data_invalid_status_string_test() ->
|
|
?assertEqual(
|
|
{error, invalid_presence},
|
|
gateway_handler_dispatch:validate_presence_data(#{
|
|
<<"status">> => <<"not_a_real_status">>
|
|
})
|
|
).
|
|
|
|
validate_presence_data_integer_status_defaults_to_online_test() ->
|
|
{ok, Result} = gateway_handler_dispatch:validate_presence_data(#{<<"status">> => 42}),
|
|
?assertEqual(online, maps:get(status, Result)).
|
|
|
|
validate_presence_data_offline_becomes_invisible_test() ->
|
|
{ok, Result} = gateway_handler_dispatch:validate_presence_data(#{
|
|
<<"status">> => <<"offline">>
|
|
}),
|
|
?assertEqual(invisible, maps:get(status, Result)).
|
|
|
|
validate_resume_data_valid_test() ->
|
|
?assertEqual(
|
|
{ok, <<"abc">>, <<"sess1">>, 5},
|
|
gateway_handler_identify:validate_resume_data(#{
|
|
<<"token">> => <<"abc">>, <<"session_id">> => <<"sess1">>, <<"seq">> => 5
|
|
})
|
|
).
|
|
validate_resume_data_missing_token_test() ->
|
|
?assertEqual(
|
|
{error, missing_required_field},
|
|
gateway_handler_identify:validate_resume_data(#{
|
|
<<"session_id">> => <<"sess1">>, <<"seq">> => 5
|
|
})
|
|
).
|
|
validate_resume_data_empty_map_test() ->
|
|
?assertEqual(
|
|
{error, missing_required_field}, gateway_handler_identify:validate_resume_data(#{})
|
|
).
|
|
validate_resume_data_not_a_map_test() ->
|
|
?assertEqual(
|
|
{error, invalid_data}, gateway_handler_identify:validate_resume_data(not_a_map())
|
|
).
|
|
|
|
validate_identify_data_empty_map_test() ->
|
|
?assertEqual(
|
|
{error, missing_required_field}, gateway_handler_identify:validate_identify_data(#{})
|
|
).
|
|
validate_identify_data_non_map_properties_test() ->
|
|
?assertEqual(
|
|
{error, invalid_properties},
|
|
gateway_handler_identify:validate_identify_data(#{
|
|
<<"token">> => <<"abc">>, <<"properties">> => <<"not_a_map">>
|
|
})
|
|
).
|
|
validate_identify_data_negative_flags_test() ->
|
|
?assertEqual(
|
|
{error, invalid_properties},
|
|
gateway_handler_identify:validate_identify_data(#{
|
|
<<"token">> => <<"abc">>,
|
|
<<"properties">> => #{
|
|
<<"os">> => <<"linux">>, <<"browser">> => <<"b">>, <<"device">> => <<"d">>
|
|
},
|
|
<<"flags">> => -1
|
|
})
|
|
).
|
|
|
|
validate_identify_data_accepts_shard_test() ->
|
|
?assertMatch(
|
|
{ok, <<"abc">>, _Properties, _Presence, [], 0, undefined, {1, 4}},
|
|
gateway_handler_identify:validate_identify_data(
|
|
valid_identify_data(#{
|
|
<<"shard">> => [1, 4]
|
|
})
|
|
)
|
|
).
|
|
|
|
validate_identify_data_accepts_absent_shard_test() ->
|
|
?assertMatch(
|
|
{ok, <<"abc">>, _Properties, _Presence, [], 0, undefined, undefined},
|
|
gateway_handler_identify:validate_identify_data(valid_identify_data(#{}))
|
|
).
|
|
|
|
validate_identify_data_rejects_invalid_shard_test() ->
|
|
?assertEqual(
|
|
{error, invalid_shard},
|
|
gateway_handler_identify:validate_identify_data(
|
|
valid_identify_data(#{
|
|
<<"shard">> => [2, 2]
|
|
})
|
|
)
|
|
),
|
|
?assertEqual(
|
|
{error, invalid_shard},
|
|
gateway_handler_identify:validate_identify_data(
|
|
valid_identify_data(#{
|
|
<<"shard">> => <<"not-a-shard">>
|
|
})
|
|
)
|
|
).
|
|
|
|
handle_identify_runs_identify_rate_check_at_zero_rollout_test() ->
|
|
session_abuse_protection:ensure_tables(),
|
|
OldConfig = gateway_rollout_config:get(),
|
|
IP = unique_test_peer_ip(<<"zero-rollout-identify">>),
|
|
try
|
|
persistent_term:put(gateway_rollout_config, OldConfig#{
|
|
<<"session_rollout_percentage">> => 0
|
|
}),
|
|
lists:foreach(
|
|
fun(_) ->
|
|
?assertEqual(ok, session_abuse_protection:check_identify_rate(IP))
|
|
end,
|
|
lists:seq(1, ?TEST_IDENTIFY_MAX_PER_IP - 1)
|
|
),
|
|
{ok, HeldState} = gateway_handler_identify:handle_identify(
|
|
valid_identify_data(#{}), IP, new_json_state()
|
|
),
|
|
cancel_pending_identify_timer(HeldState),
|
|
?assertEqual(
|
|
{error, identify_rate_limited},
|
|
session_abuse_protection:check_identify_rate(IP)
|
|
)
|
|
after
|
|
persistent_term:put(gateway_rollout_config, OldConfig)
|
|
end.
|
|
|
|
handle_session_start_result_invalid_shard_closes_4010_test() ->
|
|
{[{close, CloseCode, _Reason}], _NewState} =
|
|
gateway_handler_identify:handle_session_start_result(
|
|
{error, invalid_shard}, new_json_state()
|
|
),
|
|
?assertEqual(constants:close_code_to_num(invalid_shard), CloseCode).
|
|
|
|
handle_session_start_result_sharding_required_closes_4011_test() ->
|
|
{[{close, CloseCode, _Reason}], _NewState} =
|
|
gateway_handler_identify:handle_session_start_result(
|
|
{error, sharding_required}, new_json_state()
|
|
),
|
|
?assertEqual(constants:close_code_to_num(sharding_required), CloseCode).
|
|
|
|
identify_with_non_map_payload_closes_test() ->
|
|
State = (new_json_state())#{peer_ip => unique_test_peer_ip(<<"198.51.100.61">>)},
|
|
{[{close, CloseCode, _Reason}], _NewState} = gateway_handler_dispatch:handle_opcode(
|
|
identify, #{<<"d">> => null}, State
|
|
),
|
|
?assertEqual(constants:close_code_to_num(decode_error), CloseCode).
|
|
|
|
voice_state_update_with_non_map_payload_closes_test() ->
|
|
State = (new_json_state())#{session_pid => self()},
|
|
{[{close, CloseCode, _Reason}], _NewState} = gateway_handler_dispatch:handle_opcode(
|
|
voice_state_update, #{<<"d">> => <<"not-a-map">>}, State
|
|
),
|
|
?assertEqual(constants:close_code_to_num(decode_error), CloseCode).
|
|
|
|
new_json_state() ->
|
|
(gateway_handler:new_state())#{
|
|
version => 1, encoding => json, compress_ctx => gateway_compress:new_context(none)
|
|
}.
|
|
|
|
heartbeat_test_state() ->
|
|
(new_json_state())#{
|
|
heartbeat_state => #{
|
|
last_ack => erlang:system_time(millisecond),
|
|
waiting_for_ack => false
|
|
}
|
|
}.
|
|
|
|
valid_identify_data(Extra) ->
|
|
maps:merge(
|
|
#{
|
|
<<"token">> => <<"abc">>,
|
|
<<"properties">> => #{
|
|
<<"os">> => <<"linux">>,
|
|
<<"browser">> => <<"browser">>,
|
|
<<"device">> => <<"device">>
|
|
}
|
|
},
|
|
Extra
|
|
).
|
|
|
|
assert_reconnect_close(Result, CloseReason) ->
|
|
{[Frame, {close, CloseCode, CloseReason}], _NewState} = Result,
|
|
?assertEqual(constants:close_code_to_num(unknown_error), CloseCode),
|
|
{_FrameType, EncodedFrame} = Frame,
|
|
{ok, DecodedFrame} = gateway_codec:decode(EncodedFrame, json),
|
|
?assertEqual(constants:opcode_to_num(reconnect), maps:get(<<"op">>, DecodedFrame)).
|
|
|
|
assert_pending_identify(Result, Request, SocketPid) ->
|
|
{ok, State} = Result,
|
|
?assertEqual({Request, SocketPid}, maps:get(pending_identify, State)),
|
|
?assertMatch({_TimerRef, _Token}, maps:get(pending_identify_retry_timer, State)),
|
|
cancel_pending_identify_timer(State).
|
|
|
|
assert_held_without_pending(Result) ->
|
|
{ok, State} = Result,
|
|
?assertEqual(undefined, maps:get(pending_identify, State, undefined)),
|
|
?assertEqual(undefined, maps:get(pending_identify_retry_timer, State, undefined)).
|
|
|
|
not_a_map() ->
|
|
eqwalizer:dynamic_cast(not_a_map).
|
|
|
|
request_worker_wait_loop() ->
|
|
receive
|
|
stop -> ok
|
|
after 60000 ->
|
|
ok
|
|
end.
|
|
|
|
cancel_pending_identify_timer(State) ->
|
|
case maps:get(pending_identify_retry_timer, State, undefined) of
|
|
{TimerRef, _Token} when is_reference(TimerRef) ->
|
|
_ = erlang:cancel_timer(TimerRef, [{async, true}, {info, false}]),
|
|
ok;
|
|
TimerRef when is_reference(TimerRef) ->
|
|
_ = erlang:cancel_timer(TimerRef, [{async, true}, {info, false}]),
|
|
ok;
|
|
_ ->
|
|
ok
|
|
end.
|
|
|
|
with_rate_limits_enabled(Fun) ->
|
|
OldValue = os:getenv("FLUXER_DISABLE_RATE_LIMITS"),
|
|
os:unsetenv("FLUXER_DISABLE_RATE_LIMITS"),
|
|
try
|
|
Fun()
|
|
after
|
|
restore_env("FLUXER_DISABLE_RATE_LIMITS", OldValue)
|
|
end.
|
|
|
|
restore_env(Key, false) ->
|
|
os:unsetenv(Key);
|
|
restore_env(Key, Value) ->
|
|
os:putenv(Key, Value).
|
|
|
|
unique_test_peer_ip(Prefix) ->
|
|
Suffix = integer_to_binary(erlang:unique_integer([positive])),
|
|
<<Prefix/binary, "-", Suffix/binary>>.
|