Skip to content

Commit

Permalink
Add tests for graphql account api
Browse files Browse the repository at this point in the history
  • Loading branch information
Premwoik committed Jan 18, 2022
1 parent 744f2ee commit fe1d459
Show file tree
Hide file tree
Showing 4 changed files with 368 additions and 1 deletion.
1 change: 1 addition & 0 deletions big_tests/default.spec
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
{suites, "tests", extdisco_SUITE}.
{suites, "tests", gdpr_SUITE}.
{suites, "tests", graphql_SUITE}.
{suites, "tests", graphql_account_SUITE}.
{suites, "tests", graphql_stanza_SUITE}.
{suites, "tests", inbox_SUITE}.
{suites, "tests", inbox_extensions_SUITE}.
Expand Down
1 change: 1 addition & 0 deletions big_tests/dynamic_domains.spec
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
"at the moment mod_pubsub doesn't support dynamic domains"}.

{suites, "tests", graphql_SUITE}.
{suites, "tests", graphql_account_SUITE}.
{suites, "tests", graphql_stanza_SUITE}.

{suites, "tests", inbox_SUITE}.
Expand Down
365 changes: 365 additions & 0 deletions big_tests/tests/graphql_account_SUITE.erl
Original file line number Diff line number Diff line change
@@ -0,0 +1,365 @@
-module(graphql_account_SUITE).

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

-compile([export_all, nowarn_export_all]).

-import(distributed_helper, [mim/0, require_rpc_nodes/1, rpc/4]).
-import(graphql_helper, [execute/3, execute_auth/2, get_listener_port/1, get_listener_config/1]).

-define(NOT_EXISTING_JID, <<"unknown987@unknown">>).

suite() ->
require_rpc_nodes([mim]) ++ escalus:suite().

all() ->
[{group, user_account_handler},
{group, admin_account_handler}].

groups() ->
[{user_account_handler, [parallel], user_account_handler()},
{admin_account_handler, [], admin_account_handler()}].

user_account_handler() ->
[user_unregister,
user_change_password].

admin_account_handler() ->
[admin_list_users,
admin_count_users,
admin_get_active_users_number,
admin_check_password,
admin_check_password_hash,
admin_check_user,
admin_register_user,
admin_remove_user,
admin_ban_user,
admin_change_user_password,
admin_remove_old_users_domain,
admin_remove_old_users_all].

init_per_suite(Config) ->
Config1 = escalus:init_per_suite(Config),
dynamic_modules:save_modules(domain_helper:host_type(), Config1).

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

init_per_group(admin_account_handler, Config) ->
Config1 = escalus:create_users(Config, escalus:get_users([alice])),
graphql_helper:init_admin_handler(Config1);
init_per_group(user_account_handler, Config) ->
[{schema_endpoint, user} | Config];
init_per_group(_, Config) ->
Config.

end_per_group(admin_account_handler, Config) ->
escalus_fresh:clean(),
escalus:delete_users(Config, escalus:get_users([alice]));
end_per_group(user_account_handler, _Config) ->
escalus_fresh:clean();
end_per_group(_, _Config) ->
ok.

init_per_testcase(C, Config) when C =:= admin_remove_old_users_all;
C =:= admin_remove_old_users_domain ->
Config1 = escalus:create_users(Config, escalus:get_users([alice, bob, alice_bis])),
escalus:init_per_testcase(C, Config1);
init_per_testcase(admin_register_user = C, Config) ->
Config1 = [{user, {<<"gql_admin_registration_test">>, <<"localhost">>}} | Config],
escalus:init_per_testcase(C, Config1);
init_per_testcase(CaseName, Config) ->
escalus:init_per_testcase(CaseName, Config).

end_per_testcase(C, Config) when C =:= admin_remove_old_users_all;
C =:= admin_remove_old_users_domain ->
escalus:delete_users(Config, escalus:get_users([alice, bob, alice_bis]));
end_per_testcase(admin_register_user = C, Config) ->
{Username, Domain} = proplists:get_value(user, Config),
rpc(mim(), mongoose_account_api, unregister_user, [Domain, Username]),
escalus:end_per_testcase(C, Config);
end_per_testcase(CaseName, Config) ->
escalus:end_per_testcase(CaseName, Config).

user_unregister(Config) ->
escalus:fresh_story_with_config(Config, [{alice, 1}], fun user_unregister_story/2).

user_unregister_story(Config, Alice) ->
Ep = ?config(schema_endpoint, Config),
Password = lists:last(escalus_users:get_usp(Config, alice)),
BinJID = escalus_client:full_jid(Alice),
Creds = {BinJID, Password},
Query = <<"mutation { account { unregister } }">>,

Path = [data, account, unregister],
Resp = execute(Ep, #{query => Query}, Creds),
?assertNotEqual(nomatch, binary:match(get_ok_value(Path, Resp), <<"successfully unregistered">>)),
% ensure user is removed
AllUsers = rpc(mim(), mongoose_account_api, list_users, [<<"localhost">>]),
{U, S, _} = jid:to_lower((jid:binary_to_bare(BinJID))),
LAliceJID = <<U/binary, "@", S/binary>>,
?assertNot(lists:any(fun(Usr) -> Usr == LAliceJID end, AllUsers)).

user_change_password(Config) ->
escalus:fresh_story_with_config(Config, [{alice, 1}], fun user_change_password_story/2).

user_change_password_story(Config, Alice) ->
Ep = ?config(schema_endpoint, Config),
Password = lists:last(escalus_users:get_usp(Config, alice)),
Creds = {escalus_client:full_jid(Alice), Password},
% set empty password
Resp1 = execute(Ep, user_change_password_body(<<"">>), Creds),
?assertNotEqual(nomatch, binary:match(get_err_msg(Resp1), <<"Empty password">>)),
% set correct password
Path = [data, account, changePassword],
Resp2 = execute(Ep, user_change_password_body(<<"kaczka">>), Creds),
?assertNotEqual(nomatch, binary:match(get_ok_value(Path, Resp2), <<"Password changed">>)).

admin_list_users(Config) ->
% unknown domain
Resp = execute_auth(list_users_body(<<"unknown-domain">>), Config),
?assertEqual([], get_ok_value([data, account, listUsers], Resp)),
% domain with users
Domain = domain_helper:domain(),
Username = jid:nameprep(escalus_users:get_username(Config, alice)),
JID = <<Username/binary, "@", Domain/binary>>,
Resp2 = execute_auth(list_users_body(Domain), Config),
Users = get_ok_value([data, account, listUsers], Resp2),
?assert(lists:any(fun(U) -> U == JID end, Users)).

admin_count_users(Config) ->
% unknown domain
Resp = execute_auth(count_users_body(<<"unknown-domain">>), Config),
?assertEqual(0, get_ok_value([data, account, countUsers], Resp)),
% domain with at least one user
Domain = domain_helper:domain(),
Resp2 = execute_auth(count_users_body(Domain), Config),
?assert( 0 < get_ok_value([data, account, countUsers], Resp2)).

admin_get_active_users_number(Config) ->
% check domain without users
Resp = execute_auth(get_active_users_number_body(<<"unknown-domain">>, 5), Config),
?assertEqual(0, get_ok_value([data, account, countActiveUsers], Resp)).

admin_check_password(Config) ->
Password = lists:last(escalus_users:get_usp(Config, alice)),
BinJID = escalus_users:get_jid(Config, alice),
Path = [data, account, checkPassword],
% correct password
Resp1 = execute_auth(check_password_body(BinJID, Password), Config),
?assertMatch(#{<<"correct">> := true, <<"message">> := _}, get_ok_value(Path, Resp1)),
% incorrect password
Resp2 = execute_auth(check_password_body(BinJID, <<"incorrect_pw">>), Config),
?assertMatch(#{<<"correct">> := false, <<"message">> := _}, get_ok_value(Path, Resp2)),
% not existing user
Resp3 = execute_auth(check_password_body(?NOT_EXISTING_JID, Password), Config),
?assertEqual(null, get_ok_value(Path, Resp3)).

admin_check_password_hash(Config) ->
% This works only with plain password
% How can I create user with a plain password?
% For user with scram password or not existing user the empty string is returned.
% Thus it can be matched with empty string hash
UserSCRAM = escalus_users:get_jid(Config, alice),
EmptyHash = <<"D41D8CD98F0B24E980998ECF8427E">>,
Method = <<"md5">>,
Path = [data, account, checkPasswordHash],
% SCRAM password user
Resp1 = execute_auth(check_password_hash_body(UserSCRAM, EmptyHash, Method), Config),
?assertMatch(#{<<"correct">> := true, <<"message">> := _}, get_ok_value(Path, Resp1)),
% not existing user
Resp2 = execute_auth(check_password_hash_body(?NOT_EXISTING_JID, EmptyHash, Method), Config),
?assertMatch(#{<<"correct">> := true, <<"message">> := _}, get_ok_value(Path, Resp2)).

admin_check_user(Config) ->
BinJID = escalus_users:get_jid(Config, alice),
Path = [data, account, checkUser],
%user exist
Resp1 = execute_auth(check_user_body(BinJID), Config),
?assertMatch(#{<<"exist">> := true, <<"message">> := _}, get_ok_value(Path, Resp1)),
%user not exist
Resp2 = execute_auth(check_user_body(?NOT_EXISTING_JID), Config),
?assertMatch(#{<<"exist">> := false, <<"message">> := _}, get_ok_value(Path, Resp2)).

admin_register_user(Config) ->
Password = <<"my_password">>,
{Username, Domain} = proplists:get_value(user, Config),
Path = [data, account, registerUser],
% register new user
Resp1 = execute_auth(register_user_body(Domain, Username, Password), Config),
?assertNotEqual(nomatch, binary:match(get_ok_value(Path, Resp1), <<"successfully registered">>)),
% try to register user with existing name
Resp2 = execute_auth(register_user_body(Domain, Username, Password), Config),
?assertNotEqual(nomatch, binary:match(get_err_msg(Resp2), <<"already registered">>)).

admin_remove_user(Config) ->
% unregister user with not existing domain
Resp2 = execute_auth(remove_user_body(?NOT_EXISTING_JID), Config),
?assertNotEqual(nomatch, binary:match(get_err_msg(Resp2), <<"not exist">>)),
% unregister not existing user with existing domain
Path = [data, account, removeUser],
%Resp3 = execute_auth(remove_user_body(<<"some_user@localhost">>), Config),
%?assertNotEqual(nomatch, binary:match(get_ok_value(Path, Resp3), <<"successfully unregister">>)),
% unregister existing user
escalus:fresh_story(Config, [{alice, 1}], fun(Alice) ->
BinJID = escalus_client:full_jid(Alice),
Resp4 = execute_auth(remove_user_body(BinJID), Config),
?assertNotEqual(nomatch, binary:match(get_ok_value(Path, Resp4), <<"successfully unregister">>))
end).

admin_ban_user(Config) ->
Path = [data, account, banUser],
Reason = <<"annoying">>,
% ban not existing user
Resp1 = execute_auth(ban_user_body(?NOT_EXISTING_JID, Reason), Config),
?assertNotEqual(nomatch, binary:match(get_err_msg(Resp1), <<"not allowed">>)),
% ban existing user
escalus:fresh_story(Config, [{alice, 1}], fun(Alice) ->
BinJID = escalus_client:full_jid(Alice),
Resp2 = execute_auth(ban_user_body(BinJID, Reason), Config),
?assertNotEqual(nomatch, binary:match(get_ok_value(Path, Resp2), <<"successfully banned">>))
end).

admin_change_user_password(Config) ->
Path = [data, account, changeUserPassword],
NewPassword= <<"new password">>,
% change password of not existing user
Resp1 = execute_auth(change_user_password_body(?NOT_EXISTING_JID, NewPassword), Config),
?assertNotEqual(nomatch, binary:match(get_err_msg(Resp1), <<"not allowed">>)),
% set empty password
Resp2 = execute_auth(change_user_password_body(?NOT_EXISTING_JID, <<"">>), Config),
?assertNotEqual(nomatch, binary:match(get_err_msg(Resp2), <<"Empty password">>)),
% change password of existing user
escalus:fresh_story(Config, [{alice, 1}], fun(Alice) ->
BinJID = escalus_client:full_jid(Alice),
Resp3 = execute_auth(change_user_password_body(BinJID, NewPassword), Config),
?assertNotEqual(nomatch, binary:match(get_ok_value(Path, Resp3), <<"Password changed">>))
end).

admin_remove_old_users_domain(Config) ->
ok.
%Res = rpc(mim(), mongoose_account_api, list_users, [<<"localhost">>]),
%ct:log("~p", [Res]),
%Res2 = rpc(mim(), mongoose_account_api, list_users, [<<"localhost.bis">>]),
%ct:log("~p", [Res2]).
%ct:fail("Not implemented!").

admin_remove_old_users_all(Config) ->
ok.
%escalus:fresh_story(Config, [{alice, 1}], fun(Alice) ->
%escalus:send(Alice, escalus_stanza:presence(<<"available">>)),
%escalus:assert(is_presence, escalus:wait_for_stanza(Alice)),
%Resp1 = execute_auth(remove_old_users_body(<<"localhost">>, 0), Config),
%?assertNotEqual(nomatch, binary:match(get_account_field(removeOldUsers, Resp1), <<"Input coercion">>)),
%Res = rpc(mim(), mongoose_account_api, list_users, [<<"localhost">>]),
%ct:log("~p", [Res]),
%Res2 = rpc(mim(), mongoose_account_api, list_users, [<<"localhost.bis">>]),
%ct:log("~p", [Res2])
%end).

%% Helpers

get_err_msg(Resp) ->
get_ok_value([errors, message], Resp).

get_account_field(Field, Resp) ->
get_ok_value([data, account, Field], Resp).

get_ok_value([errors | Path], {{<<"200">>, <<"OK">>}, #{<<"errors">> := [Error]}}) ->
get_value(Path, Error);
get_ok_value(Path, {{<<"200">>, <<"OK">>}, Data}) ->
get_value(Path, Data).

get_err_value([errors | Path], {{<<"400">>, <<"Bad Request">>}, #{<<"errors">> := [Error]}}) ->
get_value(Path, Error).

get_value([], Data) -> Data;
get_value([Field | Fields], Data) ->
BinField = atom_to_binary(Field),
Data2 = maps:get(BinField, Data),
get_value(Fields, Data2).


list_users_body(Domain) ->
Query = <<"query Q1($domain: String!) { account { listUsers(domain: $domain) } }">>,
OpName = <<"Q1">>,
Vars = #{<<"domain">> => Domain},
#{query => Query, operationName => OpName, variables => Vars}.

count_users_body(Domain) ->
Query = <<"query Q1($domain: String!) { account { countUsers(domain: $domain) } }">>,
OpName = <<"Q1">>,
Vars = #{<<"domain">> => Domain},
#{query => Query, operationName => OpName, variables => Vars}.

get_active_users_number_body(Domain, Days) ->
Query = <<"query Q1($domain: String!, $days: Int!)
{ account { countActiveUsers(domain: $domain, days: $days) } }">>,
OpName = <<"Q1">>,
Vars = #{<<"domain">> => Domain, <<"days">> => Days},
#{query => Query, operationName => OpName, variables => Vars}.

check_password_body(User, Password) ->
Query = <<"query Q1($user: JID!, $password: String!)
{ account { checkPassword(user: $user, password: $password) {correct message} } }">>,
OpName = <<"Q1">>,
Vars = #{<<"user">> => User, <<"password">> => Password},
#{query => Query, operationName => OpName, variables => Vars}.

check_password_hash_body(User, PasswordHash, HashMethod) ->
Query = <<"query Q1($user: JID!, $hash: String!, $method: String!)
{ account { checkPasswordHash(user: $user, passwordHash: $hash, hashMethod: $method) {correct message} } }">>,
OpName = <<"Q1">>,
Vars = #{<<"user">> => User, <<"hash">> => PasswordHash, <<"method">> => HashMethod},
#{query => Query, operationName => OpName, variables => Vars}.

check_user_body(User) ->
Query = <<"query Q1($user: JID!)
{ account { checkUser(user: $user) {exist message} } }">>,
OpName = <<"Q1">>,
Vars = #{<<"user">> => User},
#{query => Query, operationName => OpName, variables => Vars}.

register_user_body(Domain, Username, Password) ->
Query = <<"mutation M1($domain: String!, $username: String!, $password: String!)
{ account { registerUser(domain: $domain, username: $username, password: $password)} }">>,
OpName = <<"M1">>,
Vars = #{<<"domain">> => Domain, <<"username">> => Username, <<"password">> => Password},
#{query => Query, operationName => OpName, variables => Vars}.

remove_user_body(User) ->
Query = <<"mutation M1($user: JID!)
{ account { removeUser(user: $user) } }">>,
OpName = <<"M1">>,
Vars = #{<<"user">> => User},
#{query => Query, operationName => OpName, variables => Vars}.

remove_old_users_body(Domain, Days) ->
Query = <<"mutation M1($domain: String, $days: Int!)
{ account { removeOldUsers(domain: $domain, days: $days) } }">>,
OpName = <<"M1">>,
Vars = #{<<"domain">> => Domain, <<"days">> => Days},
#{query => Query, operationName => OpName, variables => Vars}.

ban_user_body(JID, Reason) ->
Query = <<"mutation M1($user: JID!, $reason: String!)
{ account { banUser(user: $user, reason: $reason) } }">>,
OpName = <<"M1">>,
Vars = #{<<"user">> => JID, <<"reason">> => Reason},
#{query => Query, operationName => OpName, variables => Vars}.

change_user_password_body(JID, NewPassword) ->
Query = <<"mutation M1($user: JID!, $newPassword: String!)
{ account { changeUserPassword(user: $user, newPassword: $newPassword) } }">>,
OpName = <<"M1">>,
Vars = #{<<"user">> => JID, <<"newPassword">> => NewPassword},
#{query => Query, operationName => OpName, variables => Vars}.

user_change_password_body(NewPassword) ->
Query = <<"mutation M1($newPassword: String!)
{ account { changePassword(newPassword: $newPassword) } }">>,
OpName = <<"M1">>,
Vars = #{<<"newPassword">> => NewPassword},
#{query => Query, operationName => OpName, variables => Vars}.
2 changes: 1 addition & 1 deletion big_tests/tests/graphql_helper.erl
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ execute(EpName, Body, Creds) ->

execute_auth(Body, Config) ->
Ep = ?config(schema_endpoint, Config),
Opts = get_listener_opts(Ep),
Opts = ?config(listener_opts, Config),
User = proplists:get_value(username, Opts),
Password = proplists:get_value(password, Opts),
execute(Ep, Body, {User, Password}).
Expand Down

0 comments on commit fe1d459

Please sign in to comment.