Files
fluxer/fluxer_gateway/test/gateway_timings_payload_tests.erl

190 lines
7.5 KiB
Erlang

%% SPDX-License-Identifier: AGPL-3.0-or-later
-module(gateway_timings_payload_tests).
-include_lib("eunit/include/eunit.hrl").
finalize_sanitizes_legacy_runtime_metadata_test() ->
RemoteNode = '[email protected]',
with_node_pod_names(#{RemoteNode => <<"gateway-presence-0">>}, fun() ->
assert_legacy_runtime_metadata_sanitized(RemoteNode)
end).
finalize_omits_legacy_child_remote_metadata_test() ->
RemoteNode = '[email protected]',
with_node_pod_names(#{RemoteNode => <<"gateway-guilds-0">>}, fun() ->
assert_legacy_child_remote_metadata_sanitized(RemoteNode)
end).
sanitize_message_rewrites_ready_timings_without_reordering_trace_test() ->
RemoteNode = '[email protected]',
with_node_pod_names(#{RemoteNode => <<"gateway-presence-1">>}, fun() ->
Message = ready_message_with_finalized_timings(RemoteNode),
Sanitized = gateway_timings_payload:sanitize_message(Message),
#{<<"d">> := Data} = Sanitized,
Timings = require_map(maps:get(<<"_timings_gw">>, Data)),
?assertEqual(15, maps:get(<<"total_us">>, Timings)),
?assertEqual(<<"gateway-websocket-2">>, maps:get(<<"pod_name">>, Timings)),
[First, Second] = maps:get(<<"trace">>, Timings),
?assertEqual(<<"first/0">>, maps:get(<<"name">>, First)),
?assertEqual(<<"second/0">>, maps:get(<<"name">>, Second)),
Remote = require_map(maps:get(<<"remote">>, Second)),
?assertEqual(<<"presence_manager">>, maps:get(<<"operation">>, Remote)),
?assertEqual(<<"gateway-presence-1">>, maps:get(<<"pod_name">>, Remote)),
?assertNot(maps:is_key(<<"role">>, Timings)),
?assertNot(maps:is_key(<<"erlang_node_name">>, Timings)),
?assertNot(maps:is_key(<<"role">>, Remote)),
?assertNot(maps:is_key(<<"erlang_node_name">>, Remote))
end).
sanitize_message_rewrites_resumed_timings_test() ->
RemoteNode = '[email protected]',
with_node_pod_names(#{RemoteNode => <<"gateway-presence-2">>}, fun() ->
Message = (ready_message_with_finalized_timings(RemoteNode))#{<<"t">> => <<"RESUMED">>},
Sanitized = gateway_timings_payload:sanitize_message(Message),
#{<<"d">> := Data} = Sanitized,
Timings = require_map(maps:get(<<"_timings_gw">>, Data)),
?assertEqual(15, maps:get(<<"total_us">>, Timings)),
?assertEqual(<<"gateway-websocket-2">>, maps:get(<<"pod_name">>, Timings)),
[First, Second] = maps:get(<<"trace">>, Timings),
?assertEqual(<<"first/0">>, maps:get(<<"name">>, First)),
?assertEqual(<<"second/0">>, maps:get(<<"name">>, Second)),
Remote = require_map(maps:get(<<"remote">>, Second)),
?assertEqual(<<"gateway-presence-2">>, maps:get(<<"pod_name">>, Remote)),
?assertNot(maps:is_key(<<"role">>, Timings)),
?assertNot(maps:is_key(<<"erlang_node_name">>, Timings))
end).
assert_legacy_runtime_metadata_sanitized(RemoteNode) ->
Timings = gateway_timings_payload:finalize(legacy_recorder(RemoteNode)),
?assertEqual(<<"microseconds">>, maps:get(<<"unit">>, Timings)),
?assertEqual(300, maps:get(<<"total_us">>, Timings)),
?assertEqual(<<"gateway-websocket-0">>, maps:get(<<"pod_name">>, Timings)),
?assertNot(maps:is_key(<<"node_name">>, Timings)),
?assertNot(maps:is_key(<<"erlang_node_name">>, Timings)),
?assertNot(maps:is_key(<<"role">>, Timings)),
[First, Second] = maps:get(<<"trace">>, Timings),
?assertEqual(<<"first/0">>, maps:get(<<"name">>, First)),
?assertEqual(<<"second/0">>, maps:get(<<"name">>, Second)),
Remote = maps:get(<<"remote">>, Second),
?assertEqual(<<"presence_manager">>, maps:get(<<"operation">>, Remote)),
?assertEqual(<<"gateway-presence-0">>, maps:get(<<"pod_name">>, Remote)),
?assertNot(maps:is_key(<<"role">>, Remote)),
?assertNot(maps:is_key(<<"erlang_node_name">>, Remote)).
legacy_recorder(RemoteNode) ->
#{
started_at_us => gateway_timings:start() - 60000000,
started_node => '[email protected]',
steps => #{},
nodes => [],
trace => [
legacy_remote_span(RemoteNode),
#{<<"name">> => <<"first/0">>, <<"duration_us">> => 100}
],
pod_name => <<"gateway-websocket-0">>,
node_name => <<"gateway-websocket-0">>,
erlang_node_name => <<"[email protected]">>,
role => <<"websocket">>
}.
legacy_remote_span(RemoteNode) ->
#{
<<"name">> => <<"second/0">>,
<<"duration_us">> => 200,
<<"remote">> => #{
<<"operation">> => <<"presence_manager">>,
<<"role">> => <<"presence">>,
<<"erlang_node_name">> => atom_to_binary(RemoteNode, utf8)
}
}.
assert_legacy_child_remote_metadata_sanitized(RemoteNode) ->
Timings = gateway_timings_payload:finalize(legacy_child_recorder(RemoteNode)),
[Parent] = maps:get(<<"trace">>, Timings),
[Child] = maps:get(<<"children">>, Parent),
Remote = maps:get(<<"remote">>, Child),
?assertEqual(<<"guild_manager">>, maps:get(<<"operation">>, Remote)),
?assertEqual(<<"gateway-guilds-0">>, maps:get(<<"pod_name">>, Remote)),
?assertEqual(50, maps:get(<<"total_us">>, Timings)),
?assertNot(maps:is_key(<<"role">>, Remote)),
?assertNot(maps:is_key(<<"erlang_node_name">>, Remote)).
legacy_child_recorder(RemoteNode) ->
#{
started_at_us => gateway_timings:start() - 1000,
steps => #{},
nodes => [],
trace => [legacy_child_parent_span(RemoteNode)],
pod_name => <<"gateway-websocket-1">>
}.
legacy_child_parent_span(RemoteNode) ->
#{
<<"name">> => <<"parent/0">>,
<<"duration_us">> => 50,
<<"children">> => [legacy_child_span(RemoteNode)]
}.
legacy_child_span(RemoteNode) ->
#{
<<"name">> => <<"child/0">>,
<<"duration_us">> => 40,
<<"remote">> => #{
<<"operation">> => <<"guild_manager">>,
<<"erlang_node_name">> => atom_to_binary(RemoteNode, utf8),
<<"role">> => <<"guilds">>
}
}.
ready_message_with_finalized_timings(RemoteNode) ->
#{
<<"op">> => 0,
<<"t">> => <<"READY">>,
<<"s">> => 1,
<<"d">> => #{
<<"_timings_gw">> => #{
<<"unit">> => <<"microseconds">>,
<<"total_us">> => 60000000,
<<"pod_name">> => <<"gateway-websocket-2">>,
<<"role">> => <<"websocket">>,
<<"erlang_node_name">> => <<"[email protected]">>,
<<"trace">> => finalized_trace(RemoteNode)
}
}
}.
finalized_trace(RemoteNode) ->
[
#{<<"name">> => <<"first/0">>, <<"duration_us">> => 5},
#{
<<"name">> => <<"second/0">>,
<<"duration_us">> => 10,
<<"remote">> => #{
<<"operation">> => <<"presence_manager">>,
<<"role">> => <<"presence">>,
<<"erlang_node_name">> => atom_to_binary(RemoteNode, utf8)
}
}
].
require_map(Value) when is_map(Value) ->
Value;
require_map(Value) ->
error({expected_map, Value}).
with_node_pod_names(NodePodNames, Fun) ->
with_persistent_term({gateway_cluster_membership, node_pod_names}, NodePodNames, Fun).
with_persistent_term(Key, Value, Fun) ->
Previous = persistent_term:get(Key, undefined),
persistent_term:put(Key, Value),
try
Fun()
after
case Previous of
undefined -> persistent_term:erase(Key);
_ -> persistent_term:put(Key, Previous)
end
end.