mirror of
https://github.com/fluxerapp/fluxer
synced 2026-10-17 13:05:32 +09:00
feat(voice): add opt-in peer-to-peer calls and country rollouts (#3367)
This commit is contained in:
1 parent
e5a568acca
commit
bef5e33eec
215 files changed
+15840
-2693
No files matched your search
@@ -0,0 +1,242 @@
|
||||
%% SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
-module(voice_p2p_tests).
|
||||
-typing([eqwalizer]).
|
||||
|
||||
-include_lib("eunit/include/eunit.hrl").
|
||||
|
||||
peer(ConnectionId, SessionId, P2p) ->
|
||||
#{
|
||||
<<"connection_id">> => ConnectionId,
|
||||
<<"session_id">> => SessionId,
|
||||
<<"user_id">> => <<"10">>,
|
||||
<<"p2p">> => P2p
|
||||
}.
|
||||
|
||||
mesh(Count) ->
|
||||
[
|
||||
peer(integer_to_binary(Index), <<"sess-", (integer_to_binary(Index))/binary>>, true)
|
||||
|| Index <- lists:seq(1, Count)
|
||||
].
|
||||
|
||||
agreed_requires_an_explicit_true_from_a_user_test() ->
|
||||
?assert(voice_p2p:agreed(true, false)),
|
||||
?assertNot(voice_p2p:agreed(true, true)),
|
||||
?assertNot(voice_p2p:agreed(false, false)),
|
||||
?assertNot(voice_p2p:agreed(undefined, false)),
|
||||
?assertNot(voice_p2p:agreed(<<"true">>, false)).
|
||||
|
||||
declined_requires_an_explicit_false_test() ->
|
||||
?assert(voice_p2p:declined(false)),
|
||||
?assertNot(voice_p2p:declined(true)),
|
||||
?assertNot(voice_p2p:declined(undefined)),
|
||||
?assertNot(voice_p2p:declined(null)).
|
||||
|
||||
channel_mode_test() ->
|
||||
?assertEqual(empty, voice_p2p:channel_mode([])),
|
||||
?assertEqual(p2p, voice_p2p:channel_mode(mesh(2))),
|
||||
?assertEqual(sfu, voice_p2p:channel_mode([peer(<<"a">>, <<"s">>, false), #{}])),
|
||||
?assertEqual(mixed, voice_p2p:channel_mode([peer(<<"a">>, <<"s">>, false) | mesh(1)])).
|
||||
|
||||
pinned_reads_the_channel_flag_test() ->
|
||||
?assert(voice_p2p:pinned(#{<<"rtc_p2p">> => true})),
|
||||
?assertNot(voice_p2p:pinned(#{<<"rtc_p2p">> => false})),
|
||||
?assertNot(voice_p2p:pinned(#{})),
|
||||
?assertNot(voice_p2p:pinned(undefined)).
|
||||
|
||||
join_decision_for_a_live_mesh_test() ->
|
||||
?assertEqual({p2p, joiner, 2}, voice_p2p:join_decision(true, mesh(1), false, false)),
|
||||
?assertEqual({p2p, joiner, 4}, voice_p2p:join_decision(true, mesh(3), false, false)),
|
||||
?assertEqual(
|
||||
{reject, voice_p2p_consent_required},
|
||||
voice_p2p:join_decision(false, mesh(3), false, false)
|
||||
),
|
||||
?assertEqual(
|
||||
{reject, voice_p2p_consent_required},
|
||||
voice_p2p:join_decision(false, mesh(4), false, false)
|
||||
),
|
||||
?assertEqual(
|
||||
{reject, voice_channel_full}, voice_p2p:join_decision(true, mesh(4), false, false)
|
||||
).
|
||||
|
||||
join_decision_for_an_empty_channel_test() ->
|
||||
?assertEqual({p2p, initiator, 1}, voice_p2p:join_decision(true, [], false, false)),
|
||||
?assertEqual({p2p, initiator, 1}, voice_p2p:join_decision(true, [], false, true)),
|
||||
?assertEqual(sfu, voice_p2p:join_decision(false, [], false, false)),
|
||||
?assertEqual(sfu, voice_p2p:join_decision(false, [], false, true)),
|
||||
?assertEqual(sfu, voice_p2p:join_decision(true, [], true, false)),
|
||||
?assertEqual(sfu, voice_p2p:join_decision(false, [], true, true)).
|
||||
|
||||
join_decision_for_an_sfu_call_ignores_the_flag_test() ->
|
||||
Sfu = [peer(<<"a">>, <<"s">>, false)],
|
||||
?assertEqual(sfu, voice_p2p:join_decision(true, Sfu, false, false)),
|
||||
?assertEqual(sfu, voice_p2p:join_decision(false, Sfu, false, true)).
|
||||
|
||||
admit_test() ->
|
||||
Grant = {p2p, <<"conn">>, [], 2},
|
||||
Token = #{token => <<"tok">>},
|
||||
?assertEqual(Grant, voice_p2p:admit({p2p, initiator, 1}, Grant)),
|
||||
?assertEqual(Grant, voice_p2p:admit({p2p, joiner, 3}, Grant)),
|
||||
?assertEqual(sfu, voice_p2p:admit({p2p, initiator, 1}, Token)),
|
||||
?assertEqual({reject, voice_p2p_unavailable}, voice_p2p:admit({p2p, joiner, 3}, Token)),
|
||||
?assertEqual(sfu, voice_p2p:admit(sfu, Token)),
|
||||
?assertEqual({reject, voice_token_failed}, voice_p2p:admit(sfu, Grant)).
|
||||
|
||||
admit_maps_a_size_decline_to_channel_full_test() ->
|
||||
Declined = {declined, voice_channel_full},
|
||||
?assertEqual({reject, voice_channel_full}, voice_p2p:admit({p2p, joiner, 3}, Declined)),
|
||||
?assertEqual({reject, voice_channel_full}, voice_p2p:admit({p2p, initiator, 1}, Declined)),
|
||||
?assertEqual({reject, voice_token_failed}, voice_p2p:admit(sfu, Declined)).
|
||||
|
||||
invariant_violation_test() ->
|
||||
?assertEqual(ok, voice_p2p:invariant_violation([])),
|
||||
?assertEqual(ok, voice_p2p:invariant_violation(mesh(4))),
|
||||
?assertEqual({error, voice_channel_full}, voice_p2p:invariant_violation(mesh(5))),
|
||||
?assertEqual(
|
||||
{error, voice_p2p_consent_required},
|
||||
voice_p2p:invariant_violation([peer(<<"a">>, <<"s">>, false) | mesh(1)])
|
||||
),
|
||||
?assertEqual(ok, voice_p2p:invariant_violation([peer(<<"a">>, <<"s">>, false)])).
|
||||
|
||||
invariant_violation_holds_a_mesh_to_the_configured_cap_test() ->
|
||||
?assertEqual(ok, voice_p2p:invariant_violation(mesh(2), 2)),
|
||||
?assertEqual({error, voice_channel_full}, voice_p2p:invariant_violation(mesh(3), 2)),
|
||||
?assertEqual(ok, voice_p2p:invariant_violation(mesh(4), undefined)),
|
||||
?assertEqual({error, voice_channel_full}, voice_p2p:invariant_violation(mesh(5), 9)),
|
||||
?assertEqual(ok, voice_p2p:invariant_violation([peer(<<"a">>, <<"s">>, false)], 1)).
|
||||
|
||||
reports_rejection_test() ->
|
||||
?assert(voice_p2p:reports_rejection(voice_p2p_consent_required)),
|
||||
?assert(voice_p2p:reports_rejection(voice_p2p_unavailable)),
|
||||
?assert(voice_p2p:reports_rejection(voice_channel_full)),
|
||||
?assertNot(voice_p2p:reports_rejection(voice_token_failed)).
|
||||
|
||||
to_sfu_clears_the_flag_and_bumps_the_version_test() ->
|
||||
VoiceState = (peer(<<"a">>, <<"s">>, true))#{<<"version">> => 3},
|
||||
Converted = voice_p2p:to_sfu(VoiceState),
|
||||
?assertEqual(false, maps:get(<<"p2p">>, Converted)),
|
||||
?assertEqual(4, maps:get(<<"version">>, Converted)).
|
||||
|
||||
add_to_token_request_test() ->
|
||||
Request = #{<<"type">> => <<"voice_get_token">>},
|
||||
?assertEqual(Request, voice_p2p:add_to_token_request(Request, sfu, <<"SE">>)),
|
||||
?assertEqual(
|
||||
Request#{
|
||||
<<"p2p">> => true,
|
||||
<<"p2p_initiator">> => true,
|
||||
<<"p2p_participant_count">> => 1,
|
||||
<<"country_code">> => <<"SE">>
|
||||
},
|
||||
voice_p2p:add_to_token_request(Request, {p2p, initiator, 1}, <<"SE">>)
|
||||
),
|
||||
?assertEqual(
|
||||
Request#{
|
||||
<<"p2p">> => true, <<"p2p_initiator">> => false, <<"p2p_participant_count">> => 3
|
||||
},
|
||||
voice_p2p:add_to_token_request(Request, {p2p, joiner, 3}, undefined)
|
||||
).
|
||||
|
||||
grant_reads_a_p2p_grant_test() ->
|
||||
Stun = #{<<"urls">> => [<<"stun:stun.example:3478">>], <<"ignored">> => true},
|
||||
Data = #{<<"p2p">> => true, <<"connectionId">> => <<"conn-1">>, <<"iceServers">> => [Stun]},
|
||||
?assertEqual(
|
||||
{p2p, <<"conn-1">>, [#{<<"urls">> => [<<"stun:stun.example:3478">>]}], 4},
|
||||
voice_p2p:grant(Data)
|
||||
).
|
||||
|
||||
grant_reads_the_configured_cap_test() ->
|
||||
Data = #{<<"p2p">> => true, <<"connectionId">> => <<"conn-1">>, <<"iceServers">> => []},
|
||||
?assertEqual(
|
||||
{p2p, <<"conn-1">>, [], 3}, voice_p2p:grant(Data#{<<"maxParticipants">> => 3})
|
||||
),
|
||||
?assertEqual(
|
||||
{p2p, <<"conn-1">>, [], 4}, voice_p2p:grant(Data#{<<"maxParticipants">> => 9})
|
||||
),
|
||||
?assertEqual(
|
||||
{p2p, <<"conn-1">>, [], 4}, voice_p2p:grant(Data#{<<"maxParticipants">> => 0})
|
||||
).
|
||||
|
||||
grant_keeps_only_urls_of_each_ice_server_test() ->
|
||||
Server = #{
|
||||
<<"urls">> => [<<"stun:stun.example:3478">>],
|
||||
<<"username">> => <<"user">>,
|
||||
<<"credential">> => <<"secret">>
|
||||
},
|
||||
Data = #{
|
||||
<<"p2p">> => true, <<"connectionId">> => <<"conn-1">>, <<"iceServers">> => [Server]
|
||||
},
|
||||
?assertEqual(
|
||||
{p2p, <<"conn-1">>, [#{<<"urls">> => [<<"stun:stun.example:3478">>]}], 4},
|
||||
voice_p2p:grant(Data)
|
||||
).
|
||||
|
||||
grant_reads_a_size_decline_test() ->
|
||||
?assertEqual(
|
||||
{declined, voice_channel_full},
|
||||
voice_p2p:grant(#{<<"p2p">> => false, <<"declineReason">> => <<"channel_full">>})
|
||||
),
|
||||
?assertEqual(
|
||||
sfu, voice_p2p:grant(#{<<"p2p">> => false, <<"declineReason">> => <<"unknown">>})
|
||||
).
|
||||
|
||||
grant_reads_a_token_response_as_sfu_test() ->
|
||||
?assertEqual(
|
||||
sfu,
|
||||
voice_p2p:grant(#{
|
||||
<<"token">> => <<"tok">>,
|
||||
<<"endpoint">> => <<"wss://voice.example">>,
|
||||
<<"connectionId">> => <<"conn-1">>
|
||||
})
|
||||
).
|
||||
|
||||
voice_server_update_shape_test() ->
|
||||
IceServers = [#{<<"urls">> => [<<"stun:stun.example:3478">>]}],
|
||||
?assertEqual(
|
||||
#{
|
||||
<<"p2p">> => true,
|
||||
<<"ice_servers">> => IceServers,
|
||||
<<"connection_id">> => <<"conn-1">>,
|
||||
<<"channel_id">> => <<"100">>,
|
||||
<<"guild_id">> => <<"999">>
|
||||
},
|
||||
voice_p2p:voice_server_update(<<"conn-1">>, 100, 999, IceServers)
|
||||
),
|
||||
?assertNot(
|
||||
maps:is_key(
|
||||
<<"guild_id">>, voice_p2p:voice_server_update(<<"conn-1">>, 100, null, IceServers)
|
||||
)
|
||||
).
|
||||
|
||||
signal_route_allows_a_mesh_peer_test() ->
|
||||
[Sender, Target] = mesh(2),
|
||||
?assertEqual({ok, Sender, Target}, voice_p2p:signal_route(mesh(2), <<"sess-1">>, <<"2">>)).
|
||||
|
||||
signal_route_denies_a_sender_outside_the_mesh_test() ->
|
||||
?assertEqual(error, voice_p2p:signal_route(mesh(2), <<"sess-9">>, <<"2">>)).
|
||||
|
||||
signal_route_denies_an_unknown_target_test() ->
|
||||
?assertEqual(error, voice_p2p:signal_route(mesh(2), <<"sess-1">>, <<"9">>)).
|
||||
|
||||
signal_route_denies_the_senders_own_connection_test() ->
|
||||
?assertEqual(error, voice_p2p:signal_route(mesh(2), <<"sess-1">>, <<"1">>)).
|
||||
|
||||
signal_route_denies_sfu_voice_states_test() ->
|
||||
Sfu = [peer(<<"1">>, <<"sess-1">>, false), peer(<<"2">>, <<"sess-2">>, false)],
|
||||
?assertEqual(error, voice_p2p:signal_route(Sfu, <<"sess-1">>, <<"2">>)),
|
||||
Mixed = [peer(<<"1">>, <<"sess-1">>, true), peer(<<"2">>, <<"sess-2">>, false)],
|
||||
?assertEqual(error, voice_p2p:signal_route(Mixed, <<"sess-1">>, <<"2">>)).
|
||||
|
||||
signal_payload_shape_test() ->
|
||||
[Sender | _] = mesh(1),
|
||||
Data = #{<<"type">> => <<"offer">>, <<"sdp">> => <<"v=0">>},
|
||||
?assertEqual(
|
||||
#{
|
||||
<<"guild_id">> => <<"999">>,
|
||||
<<"channel_id">> => <<"100">>,
|
||||
<<"from">> => <<"1">>,
|
||||
<<"user_id">> => <<"10">>,
|
||||
<<"data">> => Data
|
||||
},
|
||||
voice_p2p:signal_payload(999, 100, Sender, Data)
|
||||
),
|
||||
?assertNot(maps:is_key(<<"guild_id">>, voice_p2p:signal_payload(null, 100, Sender, Data))).
|
||||
Reference in new issue
Block a user