Skip to content

Commit

Permalink
WIP3 happy path with tests
Browse files Browse the repository at this point in the history
  • Loading branch information
chrzaszcz committed Oct 28, 2022
1 parent 3540003 commit f01acdd
Show file tree
Hide file tree
Showing 14 changed files with 287 additions and 146 deletions.
56 changes: 46 additions & 10 deletions big_tests/tests/graphql_helper.erl
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,24 @@ execute(Node, EpName, Body, Creds) ->
body => Body},
rest_helper:make_request(Request).

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 +54,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 +93,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
68 changes: 50 additions & 18 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,9 @@ admin_stanza_cases() ->
admin_send_stanza_from_unknown_user,
admin_send_stanza_from_unknown_domain].

admin_sse_cases() ->
[admin_get_messages].

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

user_stanza_cases() ->
[user_send_message,
user_get_messages,
user_send_message_without_from,
user_send_message_with_spoofed_from,
user_send_message_headline,
Expand All @@ -89,7 +100,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 +115,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 +129,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 @@ -155,21 +166,19 @@ admin_send_message_story(Config, Alice, Bob) ->

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

admin_get_messages_story(Config, Alice, Bob) ->
From = escalus_client:full_jid(Alice),
To = escalus_client:short_jid(Bob),


{200, Stream} = get_messages(To, Config),
escalus:send(Alice, escalus_stanza:chat(From, To, <<"Hi!">>)),



Res = send_message(From, To, <<"Hi!">>, Config),
#{<<"id">> := StanzaId} = get_ok_value([data, stanza, sendMessage], Res),
assert_not_empty(StanzaId),
escalus:assert(is_message, escalus:wait_for_stanza(Bob)).
Event = sse_helper:wait_for_event(Stream),
#{<<"stanza">> := StanzaBin, <<"sender">> := From} =
graphql_helper:get_value([data, stanza, getMessages], 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}],
Expand All @@ -183,6 +192,22 @@ user_send_message_story(Config, Alice, Bob) ->
assert_not_empty(StanzaId),
escalus:assert(is_message, escalus:wait_for_stanza(Bob)).

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

user_get_messages_story(Config, Alice, Bob) ->
From = escalus_client:full_jid(Alice),
To = escalus_client:short_jid(Bob),
{200, Stream} = user_get_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, getMessages], 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 @@ -545,6 +570,13 @@ user_send_stanza(User, Stanza, Config) ->
Vars = #{stanza => Stanza},
execute_user_command(<<"stanza">>, <<"sendStanza">>, User, Vars, Config).

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

user_get_messages(User, Config) ->
execute_user_command_sse(<<"stanza">>, <<"getMessages">>, User, #{}, Config).

get_last_messages(Caller, With, Before, Config) ->
Vars = #{caller => Caller, with => With, before => Before},
execute_command(<<"stanza">>, <<"getLastMessages">>, Vars, Config).
Expand Down
58 changes: 13 additions & 45 deletions big_tests/tests/rest_client_SUITE.erl
Original file line number Diff line number Diff line change
Expand Up @@ -228,15 +228,12 @@ msg_is_sent_and_delivered_over_sse(ConfigIn) ->
Bob = escalus_users:get_userspec(Config, bob),
Alice = escalus_users:get_userspec(Config, alice),

Conn = connect_to_sse({alice, Alice}),
{200, Conn} = connect_to_sse({alice, Alice}),
M = send_message(bob, Bob, Alice),

Event = wait_for_event(Conn),
Data = jiffy:decode(maps:get(data, Event), [return_maps]),

assert_json_message(M, Data),

stop_sse(Conn).
Event = sse_helper:wait_for_event(Conn),
assert_json_message(M, Event),
sse_helper:stop_sse(Conn).

message_sending_errors(Config) ->
escalus:fresh_story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) ->
Expand All @@ -258,31 +255,27 @@ room_msg_is_sent_and_delivered_over_sse(ConfigIn) ->
RoomID = given_new_room_with_users({alice, Alice}, [{bob, Bob}]),
RoomInfo = get_room_info({alice, Alice}, RoomID),
true = is_participant(Bob, <<"member">>, RoomInfo),
Conn = connect_to_sse({bob, Bob}),
{200, Conn} = connect_to_sse({bob, Bob}),
Message = given_message_sent_to_room(RoomID, {alice, Alice}),
Event = wait_for_event(Conn),
Data = jiffy:decode(maps:get(data, Event), [return_maps]),
assert_json_room_sse_message(Message#{room => RoomID, type => <<"message">>},
Data),
stop_sse(Conn).
Event = sse_helper:wait_for_event(Conn),
assert_json_room_sse_message(Message#{room => RoomID, type => <<"message">>}, Event),
sse_helper:stop_sse(Conn).

aff_change_msg_is_delivered_over_sse(ConfigIn) ->
Config = escalus_fresh:create_users(ConfigIn, [{alice, 1}, {bob, 1}]),
Bob = escalus_users:get_userspec(Config, bob),
Alice = escalus_users:get_userspec(Config, alice),
RoomID = given_new_room({alice, Alice}),
Conn = connect_to_sse({bob, Bob}),
{200, Conn} = connect_to_sse({bob, Bob}),
given_user_invited({alice, Alice}, RoomID, Bob),
Event = wait_for_event(Conn),
Data = jiffy:decode(maps:get(data, Event), [return_maps]),
Event = sse_helper:wait_for_event(Conn),
BobJID = user_jid(Bob),
RoomJID = room_jid(RoomID, Config),
assert_json_room_sse_message(#{room => RoomID,
from => RoomJID,
type => <<"affiliation">>,
user => BobJID},
Data),
stop_sse(Conn).
user => BobJID}, Event),
sse_helper:stop_sse(Conn).

all_messages_are_archived(Config) ->
escalus:fresh_story(Config, [{alice, 1}, {bob, 1}, {kate, 1}], fun(Alice, Bob, Kate) ->
Expand Down Expand Up @@ -1132,32 +1125,7 @@ is_participant(User, Role, RoomInfo) ->

connect_to_sse(User) ->
Port = ct:get_config({hosts, mim, http_api_client_endpoint_port}),
{Username, Password} = credentials(User),
Base64 = base64:encode(binary_to_list(Username) ++ [$: | binary_to_list(Password)]),
Headers = [{<<"authorization">>, <<"basic ", Base64/binary>>},
{<<"host">>, <<"localhost">>},
{<<"accept">>, <<"text/event-stream">>}],
{ok, ConnPid} = gun:open("localhost", Port, #{
transport => tls,
protocols => [http],
http_opts => #{content_handlers => [gun_sse_h, gun_data_h]}
}),
{ok, _} = gun:await_up(ConnPid),
StreamRef = gun:get(ConnPid, "/api/sse", Headers),
#{pid => ConnPid, stream_ref => StreamRef}.

wait_for_event(#{pid := Pid, stream_ref := StreamRef} = Opts) ->
case gun:await(Pid, StreamRef) of
{response, nofin, _Status, _} ->
wait_for_event(Opts);
{sse, #{data := [Response]}} ->
Opts#{data => Response};
Error ->
Error
end.

stop_sse(#{pid := Pid}) ->
gun:close(Pid).
sse_helper:connect_to_sse(Port, "/api/sse", credentials(User), #{transport => tls}).

assert_json_message(Sent, Received) ->
#{<<"body">> := Body,
Expand Down
34 changes: 34 additions & 0 deletions big_tests/tests/sse_helper.erl
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
-module(sse_helper).

-compile([export_all, nowarn_export_all]).

connect_to_sse(Port, Path, Creds, ExtraOpts) ->
{Username, Password} = Creds,
Base64 = base64:encode(binary_to_list(Username) ++ [$: | binary_to_list(Password)]),
Headers = [{<<"authorization">>, <<"basic ", Base64/binary>>},
{<<"host">>, <<"localhost">>},
{<<"accept">>, <<"text/event-stream">>}],
Opts = maps:merge(basic_opts(), ExtraOpts),
{ok, ConnPid} = gun:open("localhost", Port, Opts),
{ok, _} = gun:await_up(ConnPid),
StreamRef = gun:get(ConnPid, Path, Headers),
{response, nofin, Status, _Headers} = gun:await(ConnPid, StreamRef),
Result = case Status of
200 ->
#{pid => ConnPid, stream_ref => StreamRef};
_ ->
{ok, Body} = gun:await_body(ConnPid, StreamRef),
jiffy:decode(Body, [return_maps])
end,
{Status, Result}.

wait_for_event(#{pid := Pid, stream_ref := StreamRef}) ->
{sse, #{data := [Response]}} = gun:await(Pid, StreamRef),
jiffy:decode(Response, [return_maps]).

stop_sse(#{pid := Pid}) ->
gun:close(Pid).

basic_opts() ->
#{protocols => [http],
http_opts => #{content_handlers => [gun_sse_h, gun_data_h]}}.
4 changes: 4 additions & 0 deletions priv/graphql/schemas/user/stanza.gql
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,7 @@ type StanzaUserMutation @protected{
"Send an arbitrary stanza"
sendStanza(stanza: Stanza): SendStanzaPayload
}

type StanzaUserSubscription {
getMessages: StanzaMap
}
Loading

0 comments on commit f01acdd

Please sign in to comment.