Skip to content

Commit

Permalink
Test the message subscriptions
Browse files Browse the repository at this point in the history
  • Loading branch information
chrzaszcz committed Nov 4, 2022
1 parent 4c3dcd9 commit 856b379
Show file tree
Hide file tree
Showing 2 changed files with 137 additions and 26 deletions.
87 changes: 69 additions & 18 deletions big_tests/tests/graphql_helper.erl
Original file line number Diff line number Diff line change
Expand Up @@ -17,20 +17,40 @@ execute(EpName, Body, Creds) ->
-spec execute(node(), atom(), binary(), {binary(), binary()} | undefined) ->
{Status :: tuple(), Data :: map()}.
execute(Node, EpName, Body, Creds) ->
Request =
#{port => get_listener_port(Node, EpName),
role => {graphql, EpName},
method => <<"POST">>,
return_maps => true,
creds => Creds,
path => "/graphql",
body => Body},
Request = build_request(Node, EpName, Body, Creds),
rest_helper:make_request(Request).

build_request(Node, EpName, Body, Creds) ->
#{port => get_listener_port(Node, EpName),
role => {graphql, EpName},
method => <<"POST">>,
return_maps => true,
creds => Creds,
path => "/graphql",
body => Body}.

execute_sse(EpName, Params, Creds) ->
#{node := Node} = mim(),
execute_sse(Node, EpName, Params, Creds).

execute_sse(Node, EpName, Params, Creds) ->
Port = get_listener_port(Node, EpName),
Path = "/api/graphql/sse",
QS = uri_string:compose_query([{atom_to_binary(K), encode_sse_value(V)}
|| {K, V} <- maps:to_list(Params)]),
sse_helper:connect_to_sse(Port, [Path, "?", QS], Creds, #{}).

encode_sse_value(M) when is_map(M) -> jiffy:encode(M);
encode_sse_value(V) when is_binary(V) -> V.

execute_user_command(Category, Command, User, Args, Config) ->
#{Category := #{commands := #{Command := #{doc := Doc}}}} = get_specs(),
Doc = get_doc(Category, Command),
execute_user(#{query => Doc, variables => Args}, User, Config).

execute_user_command_sse(Category, Command, User, Args, Config) ->
Doc = get_doc(Category, Command),
execute_user_sse(#{query => Doc, variables => Args}, User, Config).

execute_command(Category, Command, Args, Config) ->
#{node := Node} = mim(),
Protocol = ?config(protocol, Config),
Expand All @@ -40,16 +60,24 @@ execute_command(Node, Category, Command, Args, Config) ->
Protocol = ?config(protocol, Config),
execute_command(Node, Category, Command, Args, Config, Protocol).

execute_command_sse(Category, Command, Args, Config) ->
Doc = get_doc(Category, Command),
execute_auth_sse(#{query => Doc, variables => Args}, Config).

%% Admin commands can be executed as GraphQL over HTTP or with CLI (mongooseimctl)
execute_command(Node, Category, Command, Args, Config, http) ->
#{Category := #{commands := #{Command := #{doc := Doc}}}} = get_specs(),
Doc = get_doc(Category, Command),
execute_auth(Node, #{query => Doc, variables => Args}, Config);
execute_command(Node, Category, Command, Args, Config, cli) ->
CLIArgs = encode_cli_args(Args),
{Result, Code}
= mongooseimctl_helper:mongooseimctl(Node, Category, [Command | CLIArgs], Config),
{{exit_status, Code}, rest_helper:decode(Result, #{return_maps => true})}.

get_doc(Category, Command) ->
#{Category := #{commands := #{Command := #{doc := Doc}}}} = get_specs(),
Doc.

encode_cli_args(Args) ->
lists:flatmap(fun({Name, Value}) -> encode_cli_arg(Name, Value) end, maps:to_list(Args)).
encode_cli_arg(_Name, null) ->
Expand All @@ -71,21 +99,35 @@ execute_auth(Body, Config) ->
execute_auth(Node, Body, Config).

execute_auth(Node, Body, Config) ->
case Ep = ?config(schema_endpoint, Config) of
admin ->
#{username := Username, password := Password} = get_listener_opts(Ep),
execute(Node, Ep, Body, {Username, Password});
domain_admin ->
Creds = ?config(domain_admin, Config),
execute(Node, Ep, Body, Creds)
end.
Ep = ?config(schema_endpoint, Config),
execute(Node, Ep, Body, make_admin_creds(Ep, Config)).

execute_auth_sse(Body, Config) ->
#{node := Node} = mim(),
execute_auth_sse(Node, Body, Config).

execute_auth_sse(Node, Body, Config) ->
Ep = ?config(schema_endpoint, Config),
execute_sse(Node, Ep, Body, make_admin_creds(Ep, Config)).

make_admin_creds(admin = Ep, _Config) ->
#{username := Username, password := Password} = get_listener_opts(Ep),
{Username, Password};
make_admin_creds(domain_admin, Config) ->
?config(domain_admin, Config).

execute_user(Body, User, Config) ->
Ep = ?config(schema_endpoint, Config),
Creds = make_creds(User),
#{node := Node} = mim(),
execute(Node, Ep, Body, Creds).

execute_user_sse(Body, User, Config) ->
Ep = ?config(schema_endpoint, Config),
Creds = make_creds(User),
#{node := Node} = mim(),
execute_sse(Node, Ep, Body, Creds).

-spec get_listener_port(binary()) -> integer().
get_listener_port(EpName) ->
#{node := Node} = mim(),
Expand Down Expand Up @@ -173,6 +215,9 @@ get_unauthorized({Code, #{<<"errors">> := Errors}}) ->
get_bad_request({Code, _Msg}) ->
assert_response_code(bad_request, Code).

get_method_not_allowed({Code, _Msg}) ->
assert_response_code(method_not_allowed, Code).

get_coercion_err_msg({Code, #{<<"errors">> := [Error]}}) ->
assert_response_code(bad_request, Code),
?assertEqual(<<"input_coercion">>, get_value([extensions, code], Error)),
Expand All @@ -196,8 +241,14 @@ get_ok_value(Path, {Code, Data}) ->

assert_response_code(bad_request, {<<"400">>, <<"Bad Request">>}) -> ok;
assert_response_code(unauthorized, {<<"401">>, <<"Unauthorized">>}) -> ok;
assert_response_code(method_not_allowed, {<<"405">>, <<"Method Not Allowed">>}) -> ok;
assert_response_code(error, {<<"200">>, <<"OK">>}) -> ok;
assert_response_code(ok, {<<"200">>, <<"OK">>}) -> ok;
assert_response_code(bad_request, 400) -> ok;
assert_response_code(unauthorized, 401) -> ok;
assert_response_code(method_not_allowed, 405) -> ok;
assert_response_code(error, 200) -> ok;
assert_response_code(ok, 200) -> ok;
assert_response_code(bad_request, {exit_status, 1}) -> ok;
assert_response_code(error, {exit_status, 1}) -> ok;
assert_response_code(ok, {exit_status, 0}) -> ok;
Expand Down
76 changes: 68 additions & 8 deletions big_tests/tests/graphql_stanza_SUITE.erl
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
-module(graphql_stanza_SUITE).

-include_lib("common_test/include/ct.hrl").
-include_lib("eunit/include/eunit.hrl").
-include_lib("exml/include/exml.hrl").

-compile([export_all, nowarn_export_all]).

-import(distributed_helper, [mim/0, require_rpc_nodes/1]).
-import(graphql_helper, [execute_user_command/5, execute_command/4,
get_ok_value/2, get_err_code/1, get_err_msg/1, get_coercion_err_msg/1,
execute_user_command_sse/5, execute_command_sse/4,
get_ok_value/2, get_value/2,
get_err_code/1, get_err_msg/1, get_coercion_err_msg/1,
get_unauthorized/1, get_not_loaded/1]).

suite() ->
Expand All @@ -22,19 +23,25 @@ all() ->

groups() ->
[{admin_stanza_http, [], [{group, admin_mam},
{group, admin_no_mam}]},
{group, admin_no_mam_http}]},
{admin_stanza_cli, [], [{group, admin_mam},
{group, admin_no_mam}]},
{group, admin_no_mam_cli}]},
{domain_admin_stanza, [], [{group, admin_mam}, % same as for admin
{group, domain_admin_no_mam}]},
{user_stanza, [], [{group, user_mam},
{group, user_no_mam}]},
{admin_mam, [parallel], admin_mam_cases()},
{admin_no_mam, [parallel], admin_stanza_cases() ++ admin_no_mam_cases()},
{admin_no_mam_http, [parallel], admin_stanza_cases(http) ++ admin_no_mam_cases()},
{admin_no_mam_cli, [], admin_stanza_cases(cli) ++ admin_no_mam_cases()},
{domain_admin_no_mam, [parallel], domain_admin_stanza_cases() ++ admin_no_mam_cases()},
{user_mam, [parallel], user_mam_cases()},
{user_no_mam, [parallel], user_stanza_cases() ++ user_no_mam_cases()}].

admin_stanza_cases(cli) ->
admin_stanza_cases();
admin_stanza_cases(http) ->
admin_stanza_cases() ++ admin_sse_cases().

admin_stanza_cases() ->
[admin_send_message,
admin_send_message_to_unparsable_jid,
Expand All @@ -44,6 +51,10 @@ admin_stanza_cases() ->
admin_send_stanza_from_unknown_user,
admin_send_stanza_from_unknown_domain].

admin_sse_cases() ->
[admin_subscribe_for_messages,
admin_subscribe_for_messages_to_unknown_user].

admin_mam_cases() ->
[admin_get_last_messages,
admin_get_last_messages_for_unknown_user,
Expand All @@ -69,6 +80,7 @@ domain_admin_stanza_cases() ->

user_stanza_cases() ->
[user_send_message,
user_subscribe_for_messages,
user_send_message_without_from,
user_send_message_with_spoofed_from,
user_send_message_headline,
Expand All @@ -89,7 +101,6 @@ init_per_suite(Config) ->
dynamic_modules:save_modules(domain_helper:host_type(), Config2).

end_per_suite(Config) ->
escalus_fresh:clean(),
dynamic_modules:restore_modules(Config),
escalus:end_per_suite(Config).

Expand All @@ -105,7 +116,8 @@ init_per_group(GN, Config) when GN =:= admin_mam;
GN =:= domain_admin_mam;
GN =:= user_mam ->
init_mam(Config);
init_per_group(GN, Config) when GN =:= admin_no_mam;
init_per_group(GN, Config) when GN =:= admin_no_mam_http;
GN =:= admin_no_mam_cli;
GN =:= domain_admin_no_mam;
GN =:= user_no_mam ->
Mods = [{mod_mam, stopped}],
Expand All @@ -118,7 +130,7 @@ end_per_group(GN, _Config) when GN =:= admin_stanza_http;
GN =:= user_stanza ->
graphql_helper:clean();
end_per_group(_GN, _Config) ->
ok.
escalus_fresh:clean().

init_per_testcase(CaseName, Config) ->
escalus:init_per_testcase(CaseName, Config).
Expand Down Expand Up @@ -153,6 +165,31 @@ admin_send_message_story(Config, Alice, Bob) ->
assert_not_empty(StanzaId),
escalus:assert(is_message, escalus:wait_for_stanza(Bob)).

admin_subscribe_for_messages_to_unknown_user(Config) ->
Domain = domain_helper:domain(),
Res = subscribe_for_messages(<<"baduser@", Domain/binary>>, Config),
?assertEqual(<<"unknown_user">>, get_err_code(Res)),
?assertEqual(<<"User does not exist">>, get_err_msg(Res)).

admin_subscribe_for_messages(Config) ->
escalus:fresh_story_with_config(Config, [{alice, 1}, {bob, 1}],
fun admin_subscribe_for_messages_story/3).

admin_subscribe_for_messages_story(Config, Alice, Bob) ->
From = escalus_client:full_jid(Alice),
To = escalus_client:short_jid(Bob),
{200, Stream} = subscribe_for_messages(To, Config),
%% Presence should be skipped
escalus:send(Alice, escalus_stanza:presence_direct(To, <<"available">>)),
%% Message should be delivered
escalus:send(Alice, escalus_stanza:chat(From, To, <<"Hi!">>)),
Event = sse_helper:wait_for_event(Stream),
#{<<"stanza">> := StanzaBin, <<"sender">> := From} =
graphql_helper:get_value([data, stanza, subscribeForMessages], Event),
{ok, Stanza} = exml:parse(StanzaBin),
escalus:assert(is_message, Stanza),
sse_helper:stop_sse(Stream).

user_send_message(Config) ->
escalus:fresh_story_with_config(Config, [{alice, 1}, {bob, 1}],
fun user_send_message_story/3).
Expand All @@ -165,6 +202,22 @@ user_send_message_story(Config, Alice, Bob) ->
assert_not_empty(StanzaId),
escalus:assert(is_message, escalus:wait_for_stanza(Bob)).

user_subscribe_for_messages(Config) ->
escalus:fresh_story_with_config(Config, [{alice, 1}, {bob, 1}],
fun user_subscribe_for_messages_story/3).

user_subscribe_for_messages_story(Config, Alice, Bob) ->
From = escalus_client:full_jid(Alice),
To = escalus_client:short_jid(Bob),
{200, Stream} = user_subscribe_for_messages(Bob, Config),
escalus:send(Alice, escalus_stanza:chat(From, To, <<"Hi!">>)),
Event = sse_helper:wait_for_event(Stream),
#{<<"stanza">> := StanzaBin, <<"sender">> := From} =
graphql_helper:get_value([data, stanza, subscribeForMessages], Event),
{ok, Stanza} = exml:parse(StanzaBin),
escalus:assert(is_message, Stanza),
sse_helper:stop_sse(Stream).

user_send_message_without_from(Config) ->
escalus:fresh_story_with_config(Config, [{alice, 1}, {bob, 1}],
fun user_send_message_without_from_story/3).
Expand Down Expand Up @@ -527,6 +580,13 @@ user_send_stanza(User, Stanza, Config) ->
Vars = #{stanza => Stanza},
execute_user_command(<<"stanza">>, <<"sendStanza">>, User, Vars, Config).

subscribe_for_messages(Caller, Config) ->
Vars = #{caller => Caller},
execute_command_sse(<<"stanza">>, <<"subscribeForMessages">>, Vars, Config).

user_subscribe_for_messages(User, Config) ->
execute_user_command_sse(<<"stanza">>, <<"subscribeForMessages">>, User, #{}, Config).

get_last_messages(Caller, With, Before, Config) ->
Vars = #{caller => Caller, with => With, before => Before},
execute_command(<<"stanza">>, <<"getLastMessages">>, Vars, Config).
Expand Down

0 comments on commit 856b379

Please sign in to comment.