1: -module(graphql_roster_SUITE).
    2: 
    3: -compile([export_all, nowarn_export_all]).
    4: 
    5: -import(distributed_helper, [mim/0, require_rpc_nodes/1, rpc/4]).
    6: -import(graphql_helper, [execute_user_command/5, execute_command/4, get_listener_port/1,
    7:                          get_listener_config/1, get_ok_value/2, get_err_value/2, get_err_msg/1,
    8:                          get_err_msg/2, get_bad_request/1, user_to_jid/1, user_to_bin/1,
    9:                          get_unauthorized/1, get_err_code/1]).
   10: 
   11: -include_lib("eunit/include/eunit.hrl").
   12: -include_lib("../../include/mod_roster.hrl").
   13: 
   14: suite() ->
   15:     require_rpc_nodes([mim]) ++ escalus:suite().
   16: 
   17: all() ->
   18:     [{group, user_roster},
   19:      {group, admin_roster_http},
   20:      {group, admin_roster_cli},
   21:      {group, domain_admin_roster}].
   22: 
   23: groups() ->
   24:     [{user_roster, [], user_roster_tests()},
   25:      {admin_roster_http, [], admin_roster_tests()},
   26:      {admin_roster_cli, [], admin_roster_tests()},
   27:      {domain_admin_roster, [], domain_admin_tests()}].
   28: 
   29: user_roster_tests() ->
   30:     [user_add_and_delete_contact,
   31:      user_try_add_nonexistent_contact,
   32:      user_add_contacts,
   33:      user_try_delete_nonexistent_contact,
   34:      user_delete_contacts,
   35:      user_invite_accept_and_cancel_subscription,
   36:      user_decline_subscription_ask,
   37:      user_list_contacts,
   38:      user_get_contact,
   39:      user_get_nonexistent_contact
   40:     ].
   41: 
   42: admin_roster_tests() ->
   43:     [admin_add_and_delete_contact,
   44:      admin_try_add_nonexistent_contact,
   45:      admin_try_add_contact_to_nonexistent_user,
   46:      admin_try_add_contact_with_unknown_domain,
   47:      admin_add_contacts,
   48:      admin_try_delete_nonexistent_contact,
   49:      admin_try_delete_contact_with_unknown_domain,
   50:      admin_delete_contacts,
   51:      admin_invite_accept_and_cancel_subscription,
   52:      admin_decline_subscription_ask,
   53:      admin_try_subscribe_with_unknown_domain,
   54:      admin_set_mutual_subscription,
   55:      admin_set_mutual_subscription_try_connect_nonexistent_users,
   56:      admin_set_mutual_subscription_try_disconnect_nonexistent_users,
   57:      admin_subscribe_to_all,
   58:      admin_subscribe_to_all_with_wrong_user,
   59:      admin_subscribe_to_all_no_groups,
   60:      admin_subscribe_to_all_without_arguments,
   61:      admin_subscribe_all_to_all,
   62:      admin_subscribe_all_to_all_with_wrong_user,
   63:      admin_subscribe_all_to_all_no_groups,
   64:      admin_subscribe_all_to_all_without_arguments,
   65:      admin_list_contacts,
   66:      admin_list_contacts_wrong_user,
   67:      admin_get_contact,
   68:      admin_get_contact_wrong_user,
   69:      admin_subscribe_all_to_all_empty_list
   70:     ].
   71: 
   72: domain_admin_tests() ->
   73:     [admin_add_and_delete_contact,
   74:      admin_try_add_nonexistent_contact,
   75:      admin_try_add_contact_to_nonexistent_user,
   76:      domain_admin_try_add_contact_with_unknown_domain,
   77:      domain_admin_try_add_contact_no_permission,
   78:      admin_add_contacts,
   79:      admin_try_delete_nonexistent_contact,
   80:      domain_admin_try_delete_contact_with_unknown_domain,
   81:      domain_admin_try_delete_contact_no_permission,
   82:      admin_set_mutual_subscription,
   83:      domain_admin_set_mutual_subscription_try_connect_nonexistent_users,
   84:      domain_admin_set_mutual_subscription_try_connect_users_no_permission,
   85:      domain_admin_set_mutual_subscription_try_disconnect_nonexistent_users,
   86:      domain_admin_set_mutual_subscription_try_disconnect_users_no_permission,
   87:      domain_admin_subscribe_to_all_no_permission,
   88:      admin_subscribe_to_all,
   89:      domain_admin_subscribe_to_all_with_wrong_user,
   90:      admin_subscribe_to_all_no_groups,
   91:      admin_subscribe_to_all_without_arguments,
   92:      domain_admin_subscribe_all_to_all_no_permission,
   93:      admin_subscribe_all_to_all,
   94:      domain_admin_subscribe_all_to_all_with_wrong_user,
   95:      admin_subscribe_all_to_all_no_groups,
   96:      admin_subscribe_all_to_all_without_arguments,
   97:      admin_list_contacts,
   98:      domain_admin_list_contacts_wrong_user,
   99:      domain_admin_list_contacts_no_permission,
  100:      admin_get_contact,
  101:      domain_admin_get_contact_wrong_user,
  102:      domain_admin_get_contacts_no_permission
  103:     ].
  104: 
  105: init_per_suite(Config) ->
  106:     Config1 = ejabberd_node_utils:init(mim(), Config),
  107:     Config2 = escalus:init_per_suite(Config1),
  108:     dynamic_modules:save_modules(domain_helper:host_type(), Config2).
  109: 
  110: end_per_suite(Config) ->
  111:     dynamic_modules:restore_modules(Config),
  112:     escalus:end_per_suite(Config).
  113: 
  114: init_per_group(admin_roster_http, Config) ->
  115:     graphql_helper:init_admin_handler(Config);
  116: init_per_group(admin_roster_cli, Config) ->
  117:     graphql_helper:init_admin_cli(Config);
  118: init_per_group(domain_admin_roster, Config) ->
  119:     graphql_helper:init_domain_admin_handler(Config);
  120: init_per_group(user_roster, Config) ->
  121:     graphql_helper:init_user(Config).
  122: 
  123: end_per_group(_GroupName, _Config) ->
  124:     graphql_helper:clean(),
  125:     escalus_fresh:clean().
  126: 
  127: init_per_testcase(CaseName, Config) ->
  128:     escalus:init_per_testcase(CaseName, Config).
  129: 
  130: end_per_testcase(CaseName, Config) ->
  131:     escalus:end_per_testcase(CaseName, Config).
  132: 
  133: -define(ADD_CONTACT_PATH, [data, roster, addContact]).
  134: -define(ADD_CONTACTS_PATH, [data, roster, addContacts]).
  135: -define(DELETE_CONTACT_PATH, [data, roster, deleteContact]).
  136: -define(DELETE_CONTACTS_PATH, [data, roster, deleteContacts]).
  137: -define(LIST_CONTACTS_PATH, [data, roster, listContacts]).
  138: -define(GET_CONTACT_PATH, [data, roster, getContact]).
  139: -define(SUBSCRIBE_ALL_TO_ALL_PATH, [data, roster, subscribeAllToAll]).
  140: -define(SUBSCRIBE_TO_ALL_PATH, [data, roster, subscribeToAll]).
  141: -define(MUTUAL_SUBSCRIPTION_PATH, [data, roster, setMutualSubscription]).
  142: 
  143: -define(NONEXISTENT_DOMAIN_USER, <<"abc@abc">>).
  144: -define(NONEXISTENT_USER, <<"abc@", (domain_helper:domain())/binary>>).
  145: -define(NONEXISTENT_USER2, <<"abc2@", (domain_helper:domain())/binary>>).
  146: -define(DEFAULT_GROUPS, [<<"Family">>]).
  147: 
  148: %% Admin test cases
  149: 
  150: admin_add_and_delete_contact(Config) ->
  151:     escalus:fresh_story_with_config(Config, [{alice, 1}, {bob, 1}],
  152:                                     fun admin_add_and_delete_contact_story/3).
  153: 
  154: admin_add_and_delete_contact_story(Config, Alice, Bob) ->
  155:     Res = admin_add_contact(Alice, Bob, Config),
  156:     ?assertNotEqual(nomatch, binary:match(get_ok_value(?ADD_CONTACT_PATH, Res),
  157:                                           <<"successfully">>)),
  158:     check_contacts([Bob], Alice),
  159: 
  160:     Res2 = admin_delete_contact(Alice, Bob, Config),
  161:     ?assertNotEqual(nomatch, binary:match(get_ok_value(?DELETE_CONTACT_PATH, Res2),
  162:                                           <<"successfully">>)),
  163:     check_contacts([], Alice).
  164: 
  165: admin_try_add_nonexistent_contact(Config) ->
  166:     escalus:fresh_story_with_config(Config, [{alice, 1}], fun admin_try_add_nonexistent_contact/2).
  167: 
  168: admin_try_add_nonexistent_contact(Config, Alice) ->
  169:     Contact = ?NONEXISTENT_DOMAIN_USER,
  170:     Res = admin_add_contact(Alice, Contact, Config),
  171:     ?assertNotEqual(nomatch, binary:match(get_err_msg(Res), Contact)),
  172:     check_contacts([], Alice).
  173: 
  174: admin_try_add_contact_to_nonexistent_user(Config) ->
  175:     User = ?NONEXISTENT_USER,
  176:     Contact = ?NONEXISTENT_USER2,
  177:     Res = admin_add_contact(User, Contact, Config),
  178:     ?assertEqual(<<"user_not_exist">>, get_err_code(Res)).
  179: 
  180: admin_try_add_contact_with_unknown_domain(Config) ->
  181:     User = ?NONEXISTENT_DOMAIN_USER,
  182:     Contact = ?NONEXISTENT_USER2,
  183:     Res = admin_add_contact(User, Contact, Config),
  184:     ?assertNotEqual(nomatch, binary:match(get_err_msg(Res), <<"not found">>)).
  185: 
  186: admin_try_delete_nonexistent_contact(Config) ->
  187:     escalus:fresh_story_with_config(Config, [{alice, 1}, {bob, 1}],
  188:                                     fun admin_try_delete_nonexistent_contact_story/3).
  189: 
  190: admin_try_delete_nonexistent_contact_story(Config, Alice, Bob) ->
  191:     Res = admin_delete_contact(Alice, Bob, Config),
  192:     ?assertNotEqual(nomatch, binary:match(get_err_msg(Res), <<"not exist">>)).
  193: 
  194: admin_try_delete_contact_with_unknown_domain(Config) ->
  195:     User = ?NONEXISTENT_DOMAIN_USER,
  196:     Res = admin_delete_contact(User, User, Config),
  197:     ?assertNotEqual(nomatch, binary:match(get_err_msg(Res), <<"not found">>)).
  198: 
  199: admin_add_contacts(Config) ->
  200:     escalus:fresh_story_with_config(Config, [{alice, 1}, {bob, 1}],
  201:                                     fun admin_add_contacts_story/3).
  202: 
  203: admin_add_contacts_story(Config, Alice, Bob) ->
  204:     Res = admin_add_contacts(Alice, [Bob, ?NONEXISTENT_DOMAIN_USER], Config),
  205:     [R1, null] = get_err_value(?ADD_CONTACTS_PATH, Res),
  206:     ?assertNotEqual(nomatch, binary:match(R1, <<"successfully">>)),
  207:     ?assertNotEqual(nomatch, binary:match(get_err_msg(Res), <<"does not exist">>)).
  208: 
  209: admin_delete_contacts(Config) ->
  210:     escalus:fresh_story_with_config(Config, [{alice, 1}, {bob, 1}],
  211:                                     fun admin_delete_contacts_story/3).
  212: 
  213: admin_delete_contacts_story(Config, Alice, Bob) ->
  214:     admin_add_contacts(Alice, [Bob], Config),
  215:     Res = admin_delete_contacts(Alice, [Bob, ?NONEXISTENT_DOMAIN_USER], Config),
  216:     [R1, null] = get_err_value(?DELETE_CONTACTS_PATH, Res),
  217:     ?assertNotEqual(nomatch, binary:match(R1, <<"successfully">>)),
  218:     ?assertNotEqual(nomatch, binary:match(get_err_msg(Res), <<"does not exist">>)).
  219: 
  220: admin_invite_accept_and_cancel_subscription(Config) ->
  221:     escalus:fresh_story_with_config(Config, [{alice, 1}, {bob, 1}],
  222:                                     fun admin_invite_accept_and_cancel_subscription_story/3).
  223: 
  224: admin_invite_accept_and_cancel_subscription_story(Config, Alice, Bob) ->
  225:     % Add contacts
  226:     admin_add_contact(Alice, Bob, Config),
  227:     admin_add_contact(Bob, Alice, Config),
  228:     escalus:assert(is_roster_set, escalus:wait_for_stanza(Bob, 1)),
  229:     escalus:assert(is_roster_set, escalus:wait_for_stanza(Alice, 1)),
  230:     % Send invitation to subscribe
  231:     admin_subscription(Alice, Bob, <<"INVITE">>, Config),
  232:     escalus:assert(is_roster_set, escalus:wait_for_stanza(Alice)),
  233:     escalus:assert(is_presence_with_type, [<<"subscribe">>], escalus:wait_for_stanza(Bob)),
  234:     ?assertMatch(#roster{ask = out, subscription = none}, get_roster(Alice, Bob)),
  235:     % Accept invitation
  236:     admin_subscription(Bob, Alice, <<"ACCEPT">>, Config),
  237:     escalus:assert(is_roster_set, escalus:wait_for_stanza(Bob)),
  238:     IsSub = fun(S) -> escalus_pred:is_presence_with_type(<<"subscribed">>, S) end,
  239:     escalus:assert_many([is_roster_set, IsSub, is_presence],
  240:                         escalus:wait_for_stanzas(Alice, 3)),
  241:     ?assertMatch(#roster{ask = none, subscription = from}, get_roster(Bob, Alice)),
  242:     % Cancel subscription
  243:     admin_subscription(Alice, Bob, <<"CANCEL">>, Config),
  244:     escalus:assert(is_roster_set, escalus:wait_for_stanza(Alice)),
  245:     ?assertMatch(#roster{ask = none, subscription = none}, get_roster(Alice, Bob)).
  246: 
  247: admin_decline_subscription_ask(Config) ->
  248:     escalus:fresh_story_with_config(Config, [{alice, 1}, {bob, 1}],
  249:                                     fun admin_decline_subscription_ask_story/3).
  250: 
  251: admin_decline_subscription_ask_story(Config, Alice, Bob) ->
  252:     % Add contacts
  253:     admin_add_contact(Alice, Bob, null, null, Config),
  254:     admin_add_contact(Bob, Alice, null, null, Config),
  255:     escalus:assert(is_roster_set, escalus:wait_for_stanza(Bob, 1)),
  256:     escalus:assert(is_roster_set, escalus:wait_for_stanza(Alice, 1)),
  257:     % Send invitation to subscribe
  258:     admin_subscription(Bob, Alice, <<"INVITE">>, Config),
  259:     ?assertMatch(#roster{ask = in, subscription = none}, get_roster(Alice, Bob)),
  260:     ?assertMatch(#roster{ask = out, subscription = none}, get_roster(Bob, Alice)),
  261:     % Decline the invitation
  262:     admin_subscription(Alice, Bob, <<"DECLINE">>, Config),
  263:     ?assertMatch(does_not_exist, get_roster(Alice, Bob)),
  264:     ?assertMatch(#roster{ask = none, subscription = none}, get_roster(Bob, Alice)).
  265: 
  266: admin_try_subscribe_with_unknown_domain(Config) ->
  267:     Bob = ?NONEXISTENT_DOMAIN_USER,
  268:     Alice = ?NONEXISTENT_USER,
  269:     Res = admin_subscription(Bob, Alice, <<"INVITE">>, Config),
  270:     ?assertNotEqual(nomatch, binary:match(get_err_msg(Res), <<"not found">>)).
  271: 
  272: admin_set_mutual_subscription(Config) ->
  273:     escalus:fresh_story_with_config(Config, [{alice, 1}, {bob, 1}],
  274:                                     fun admin_set_mutual_subscription_story/3).
  275: 
  276: admin_set_mutual_subscription_story(Config, Alice, Bob) ->
  277:     Res = admin_mutual_subscription(Alice, Bob, <<"CONNECT">>, Config),
  278:     ?assertNotEqual(nomatch, binary:match(get_ok_value(?MUTUAL_SUBSCRIPTION_PATH, Res),
  279:                                           <<"successfully">>)),
  280:     ?assertMatch(#roster{ask = none, subscription = both}, get_roster(Alice, Bob)),
  281:     ?assertMatch(#roster{ask = none, subscription = both}, get_roster(Bob, Alice)),
  282: 
  283:     Res2 = admin_mutual_subscription(Alice, Bob, <<"DISCONNECT">>, Config),
  284:     ?assertNotEqual(nomatch, binary:match(get_ok_value(?MUTUAL_SUBSCRIPTION_PATH, Res2),
  285:                                           <<"successfully">>)),
  286:     ?assertMatch(does_not_exist, get_roster(Alice, Bob)),
  287:     ?assertMatch(does_not_exist, get_roster(Bob, Alice)).
  288: 
  289: admin_set_mutual_subscription_try_connect_nonexistent_users(Config) ->
  290:     Alice = ?NONEXISTENT_DOMAIN_USER,
  291:     Bob = ?NONEXISTENT_USER,
  292:     Res = admin_mutual_subscription(Alice, Bob, <<"CONNECT">>, Config),
  293:     ?assertNotEqual(nomatch, binary:match(get_err_msg(Res), <<"not found">>)).
  294: 
  295: admin_set_mutual_subscription_try_disconnect_nonexistent_users(Config) ->
  296:     Alice = ?NONEXISTENT_DOMAIN_USER,
  297:     Bob = ?NONEXISTENT_USER,
  298:     Res = admin_mutual_subscription(Alice, Bob, <<"DISCONNECT">>, Config),
  299:     ?assertNotEqual(nomatch, binary:match(get_err_msg(Res), <<"not found">>)).
  300: 
  301: admin_subscribe_to_all(Config) ->
  302:     escalus:fresh_story_with_config(Config, [{alice, 1}, {bob, 1}, {kate, 1}],
  303:                                     fun admin_subscribe_to_all_story/4).
  304: 
  305: admin_subscribe_to_all_story(Config, Alice, Bob, Kate) ->
  306:     Res = admin_subscribe_to_all(Alice, [Bob, Kate], Config),
  307:     check_if_created_succ(?SUBSCRIBE_TO_ALL_PATH, Res),
  308: 
  309:     check_contacts([Bob, Kate], Alice),
  310:     check_contacts([Alice], Bob),
  311:     check_contacts([Alice], Kate).
  312: 
  313: admin_subscribe_to_all_with_wrong_user(Config) ->
  314:     escalus:fresh_story_with_config(Config, [{alice, 1}, {bob, 1}],
  315:                                     fun admin_subscribe_to_all_with_wrong_user_story/3).
  316: 
  317: admin_subscribe_to_all_with_wrong_user_story(Config, Alice, Bob) ->
  318:     Kate = ?NONEXISTENT_DOMAIN_USER,
  319:     Res = admin_subscribe_to_all(Alice, [Bob, Kate], Config),
  320:     check_if_created_succ(?SUBSCRIBE_TO_ALL_PATH, Res, [true, false]),
  321:     ?assertNotEqual(nomatch, binary:match(get_err_msg(Res), <<"does not exist">>)),
  322: 
  323:     check_contacts([Bob], Alice),
  324:     check_contacts([Alice], Bob).
  325: 
  326: admin_subscribe_to_all_no_groups(Config) ->
  327:     escalus:fresh_story_with_config(Config, [{alice, 1}, {bob, 1}, {kate, 1}],
  328:                                     fun admin_subscribe_to_all_no_groups_story/4).
  329: 
  330: admin_subscribe_to_all_no_groups_story(Config, Alice, Bob, Kate) ->
  331:     EmptyGroups = [],
  332:     Res = admin_subscribe_to_all(Alice, [Bob, Kate], null, Config),
  333:     check_if_created_succ(?SUBSCRIBE_TO_ALL_PATH, Res),
  334: 
  335:     check_contacts([Bob, Kate], Alice, EmptyGroups),
  336:     check_contacts([Alice], Bob, EmptyGroups),
  337:     check_contacts([Alice], Kate, EmptyGroups).
  338: 
  339: admin_subscribe_to_all_without_arguments(Config) ->
  340:     Res = admin_subscribe_to_all_no_args(Config),
  341:     get_bad_request(Res).
  342: 
  343: admin_subscribe_all_to_all(Config) ->
  344:     escalus:fresh_story_with_config(Config, [{alice, 1}, {bob, 1}, {kate, 1}],
  345:                                     fun admin_subscribe_all_to_all_story/4).
  346: 
  347: admin_subscribe_all_to_all_story(Config, Alice, Bob, Kate) ->
  348:     Res = admin_subscribe_all_to_all([Alice, Bob, Kate], Config),
  349:     check_if_created_succ(?SUBSCRIBE_ALL_TO_ALL_PATH, Res),
  350: 
  351:     check_contacts([Bob, Kate], Alice),
  352:     check_contacts([Alice, Kate], Bob),
  353:     check_contacts([Alice, Bob], Kate).
  354: 
  355: admin_subscribe_all_to_all_with_wrong_user(Config) ->
  356:     escalus:fresh_story_with_config(Config, [{alice, 1}, {bob, 1}],
  357:                                     fun admin_subscribe_all_to_all_with_wrong_user_story/3).
  358: 
  359: admin_subscribe_all_to_all_with_wrong_user_story(Config, Alice, Bob) ->
  360:     Kate = ?NONEXISTENT_DOMAIN_USER,
  361:     Res = admin_subscribe_all_to_all([Alice, Bob, Kate], Config),
  362:     check_if_created_succ(?SUBSCRIBE_ALL_TO_ALL_PATH, Res, [true, false, false]),
  363:     ?assertNotEqual(nomatch, binary:match(get_err_msg(1, Res), <<"does not exist">>)),
  364:     ?assertNotEqual(nomatch, binary:match(get_err_msg(2, Res), <<"does not exist">>)),
  365: 
  366:     check_contacts([Bob], Alice),
  367:     check_contacts([Alice], Bob).
  368: 
  369: admin_subscribe_all_to_all_no_groups(Config) ->
  370:     escalus:fresh_story_with_config(Config, [{alice, 1}, {bob, 1}, {kate, 1}],
  371:                                     fun admin_subscribe_all_to_all_no_groups_story/4).
  372: 
  373: admin_subscribe_all_to_all_no_groups_story(Config, Alice, Bob, Kate) ->
  374:     EmptyGroups = [],
  375:     Res = admin_subscribe_all_to_all([Alice, Bob, Kate], null, Config),
  376:     check_if_created_succ(?SUBSCRIBE_ALL_TO_ALL_PATH, Res),
  377: 
  378:     check_contacts([Bob, Kate], Alice, EmptyGroups),
  379:     check_contacts([Alice, Kate], Bob, EmptyGroups),
  380:     check_contacts([Alice, Bob], Kate, EmptyGroups).
  381: 
  382: admin_subscribe_all_to_all_without_arguments(Config) ->
  383:     Res = admin_subscribe_all_to_all_no_args(Config),
  384:     get_bad_request(Res).
  385: 
  386: admin_list_contacts(Config) ->
  387:     escalus:fresh_story_with_config(Config, [{alice, 1}, {bob, 1}],
  388:                                     fun admin_list_contacts_story/3).
  389: 
  390: admin_list_contacts_story(Config, Alice, Bob) ->
  391:     BobBin = escalus_utils:jid_to_lower(escalus_client:short_jid(Bob)),
  392:     BobName = escalus_client:username(Bob),
  393:     admin_add_contact(Alice, Bob, Config),
  394:     Res = admin_list_contacts(Alice, Config),
  395:     [#{<<"subscription">> := <<"NONE">>, <<"ask">> := <<"NONE">>, <<"jid">> := BobBin,
  396:        <<"name">> := BobName, <<"groups">> := ?DEFAULT_GROUPS}] =
  397:         get_ok_value([data, roster, listContacts], Res).
  398: 
  399: admin_list_contacts_wrong_user(Config) ->
  400:     % User with a non-existent domain
  401:     Res = admin_list_contacts(?NONEXISTENT_DOMAIN_USER, Config),
  402:     ?assertNotEqual(nomatch, binary:match(get_err_msg(Res), <<"not found">>)),
  403:     % Non-existent user with existent domain
  404:     Res2 = admin_list_contacts(?NONEXISTENT_USER, Config),
  405:     ?assertEqual(<<"user_not_exist">>, get_err_code(Res2)).
  406: 
  407: admin_get_contact(Config) ->
  408:     escalus:fresh_story_with_config(Config, [{alice, 1}, {bob, 1}],
  409:                                     fun admin_get_contact_story/3).
  410: 
  411: admin_get_contact_story(Config, Alice, Bob) ->
  412:     BobBin = escalus_utils:jid_to_lower(escalus_client:short_jid(Bob)),
  413:     BobName = escalus_client:username(Bob),
  414:     admin_add_contact(Alice, Bob, Config),
  415:     Res = admin_get_contact(Alice, Bob, Config),
  416:     #{<<"subscription">> := <<"NONE">>, <<"ask">> := <<"NONE">>, <<"jid">> := BobBin,
  417:       <<"name">> := BobName, <<"groups">> := ?DEFAULT_GROUPS} =
  418:         get_ok_value([data, roster, getContact], Res).
  419: 
  420: admin_get_contact_wrong_user(Config) ->
  421:     % User with a non-existent domain
  422:     Res = admin_get_contact(?NONEXISTENT_DOMAIN_USER, ?NONEXISTENT_USER, Config),
  423:     ?assertNotEqual(nomatch, binary:match(get_err_msg(Res), <<"not found">>)),
  424:     % Non-existent user with existent domain
  425:     Res2 = admin_get_contact(?NONEXISTENT_USER, ?NONEXISTENT_USER, Config),
  426:     ?assertNotEqual(nomatch, binary:match(get_err_msg(Res2), <<"does not exist">>)).
  427: 
  428: admin_subscribe_all_to_all_empty_list(Config) ->
  429:     Res = admin_subscribe_all_to_all([], Config),
  430:     ?assertEqual([], get_ok_value(?SUBSCRIBE_ALL_TO_ALL_PATH, Res)).
  431: 
  432: %% User test cases
  433: 
  434: user_add_and_delete_contact(Config) ->
  435:     escalus:fresh_story_with_config(Config, [{alice, 1}, {bob, 1}],
  436:                                     fun user_add_and_delete_contact_story/3).
  437: 
  438: user_add_and_delete_contact_story(Config, Alice, Bob) ->
  439:     % Add a new contact
  440:     Res = user_add_contact(Alice, Bob, Config),
  441:     ?assertNotEqual(nomatch, binary:match(get_ok_value(?ADD_CONTACT_PATH, Res),
  442:                                           <<"successfully">>)),
  443:     check_contacts([Bob], Alice),
  444:     % Delete a contact
  445:     Res2 = user_delete_contact(Alice, Bob, Config),
  446:     ?assertNotEqual(nomatch, binary:match(get_ok_value(?DELETE_CONTACT_PATH, Res2),
  447:                                           <<"successfully">>)),
  448:     check_contacts([], Alice).
  449: 
  450: user_try_add_nonexistent_contact(Config) ->
  451:     escalus:fresh_story_with_config(Config, [{alice, 1}], fun user_try_add_nonexistent_contact/2).
  452: 
  453: user_try_add_nonexistent_contact(Config, Alice) ->
  454:     Contact = ?NONEXISTENT_DOMAIN_USER,
  455:     Res = user_add_contact(Alice, Contact, Config),
  456:     ?assertNotEqual(nomatch, binary:match(get_err_msg(Res), Contact)),
  457:     check_contacts([], Alice).
  458: 
  459: user_add_contacts(Config) ->
  460:     escalus:fresh_story_with_config(Config, [{alice, 1}, {bob, 1}],
  461:                                     fun user_add_contacts_story/3).
  462: 
  463: user_add_contacts_story(Config, Alice, Bob) ->
  464:     Res = user_add_contacts(Alice, [Bob, ?NONEXISTENT_DOMAIN_USER], Config),
  465:     [R1, null] = get_ok_value(?ADD_CONTACTS_PATH, Res),
  466:     ?assertNotEqual(nomatch, binary:match(R1, <<"successfully">>)),
  467:     ?assertNotEqual(nomatch, binary:match(get_err_msg(Res), <<"does not exist">>)).
  468: 
  469: user_try_delete_nonexistent_contact(Config) ->
  470:     escalus:fresh_story_with_config(Config, [{alice, 1}, {bob, 1}],
  471:                                     fun user_try_delete_nonexistent_contact_story/3).
  472: 
  473: user_try_delete_nonexistent_contact_story(Config, Alice, Bob) ->
  474:     Res = user_delete_contact(Alice, Bob, Config),
  475:     ?assertNotEqual(nomatch, binary:match(get_err_msg(Res), <<"not exist">>)).
  476: 
  477: user_delete_contacts(Config) ->
  478:     escalus:fresh_story_with_config(Config, [{alice, 1}, {bob, 1}],
  479:                                     fun user_delete_contacts_story/3).
  480: 
  481: user_delete_contacts_story(Config, Alice, Bob) ->
  482:     user_add_contact(Alice, Bob, Config),
  483: 
  484:     Res = user_delete_contacts(Alice, [Bob, ?NONEXISTENT_DOMAIN_USER], Config),
  485:     [R1, null] = get_ok_value(?DELETE_CONTACTS_PATH, Res),
  486:     ?assertNotEqual(nomatch, binary:match(R1, <<"successfully">>)),
  487:     ?assertNotEqual(nomatch, binary:match(get_err_msg(Res), <<"does not exist">>)).
  488: 
  489: user_invite_accept_and_cancel_subscription(Config) ->
  490:     escalus:fresh_story_with_config(Config, [{alice, 1}, {bob, 1}],
  491:                                     fun user_invite_accept_and_cancel_subscription_story/3).
  492: 
  493: user_invite_accept_and_cancel_subscription_story(Config, Alice, Bob) ->
  494:     % Add contacts
  495:     user_add_contact(Alice, Bob, Config),
  496:     user_add_contact(Bob, Alice, Config),
  497:     escalus:assert(is_roster_set, escalus:wait_for_stanza(Bob, 1)),
  498:     escalus:assert(is_roster_set, escalus:wait_for_stanza(Alice, 1)),
  499:     % Send invitation to subscribe
  500:     user_subscription(Alice, Bob, <<"INVITE">>, Config),
  501:     escalus:assert(is_roster_set, escalus:wait_for_stanza(Alice)),
  502:     escalus:assert(is_presence_with_type, [<<"subscribe">>], escalus:wait_for_stanza(Bob)),
  503:     ?assertMatch(#roster{ask = out, subscription = none}, get_roster(Alice, Bob)),
  504:     % Accept invitation
  505:     user_subscription(Bob, Alice, <<"ACCEPT">>, Config),
  506:     escalus:assert(is_roster_set, escalus:wait_for_stanza(Bob)),
  507:     IsSub = fun(S) -> escalus_pred:is_presence_with_type(<<"subscribed">>, S) end,
  508:     escalus:assert_many([is_roster_set, IsSub, is_presence],
  509:                         escalus:wait_for_stanzas(Alice, 3)),
  510:     ?assertMatch(#roster{ask = none, subscription = from}, get_roster(Bob, Alice)),
  511:     % Cancel subscription
  512:     user_subscription(Alice, Bob, <<"CANCEL">>, Config),
  513:     escalus:assert(is_roster_set, escalus:wait_for_stanza(Alice)),
  514:     ?assertMatch(#roster{ask = none, subscription = none}, get_roster(Alice, Bob)).
  515: 
  516: user_decline_subscription_ask(Config) ->
  517:     escalus:fresh_story_with_config(Config, [{alice, 1}, {bob, 1}],
  518:                                     fun user_decline_subscription_ask_story/3).
  519: 
  520: user_decline_subscription_ask_story(Config, Alice, Bob) ->
  521:     % Add contacts
  522:     user_add_contact(Alice, Bob, Config),
  523:     user_add_contact(Bob, Alice, Config),
  524:     escalus:assert(is_roster_set, escalus:wait_for_stanza(Bob, 1)),
  525:     escalus:assert(is_roster_set, escalus:wait_for_stanza(Alice, 1)),
  526:     % Send invitation to subscribe
  527:     user_subscription(Bob, Alice, <<"INVITE">>, Config),
  528:     ?assertMatch(#roster{ask = in, subscription = none}, get_roster(Alice, Bob)),
  529:     ?assertMatch(#roster{ask = out, subscription = none}, get_roster(Bob, Alice)),
  530:     % Decline the invitation
  531:     user_subscription(Alice, Bob, <<"DECLINE">>, Config),
  532:     ?assertMatch(does_not_exist, get_roster(Alice, Bob)),
  533:     ?assertMatch(#roster{ask = none, subscription = none}, get_roster(Bob, Alice)).
  534: 
  535: user_list_contacts(Config) ->
  536:     escalus:fresh_story_with_config(Config, [{alice, 1}, {bob, 1}],
  537:                                     fun user_list_contacts_story/3).
  538: 
  539: user_list_contacts_story(Config, Alice, Bob) ->
  540:     BobBin = escalus_utils:jid_to_lower(escalus_client:short_jid(Bob)),
  541:     Name = <<"Bobek">>,
  542:     user_add_contact(Alice, Bob, Name, ?DEFAULT_GROUPS, Config),
  543:     Res = user_list_contacts(Alice, Config),
  544:     [#{<<"subscription">> := <<"NONE">>, <<"ask">> := <<"NONE">>, <<"jid">> := BobBin,
  545:        <<"name">> := Name, <<"groups">> := ?DEFAULT_GROUPS}] =
  546:         get_ok_value(?LIST_CONTACTS_PATH, Res).
  547: 
  548: user_get_contact(Config) ->
  549:     escalus:fresh_story_with_config(Config, [{alice, 1}, {bob, 1}],
  550:                                     fun user_get_contact_story/3).
  551: 
  552: user_get_contact_story(Config, Alice, Bob) ->
  553:     BobBin = escalus_utils:jid_to_lower(escalus_client:short_jid(Bob)),
  554:     Name = <<"Bobek">>,
  555:     user_add_contact(Alice, Bob, Name, ?DEFAULT_GROUPS, Config),
  556:     Res = user_get_contact(Alice, Bob, Config),
  557:     #{<<"subscription">> := <<"NONE">>, <<"ask">> := <<"NONE">>, <<"jid">> := BobBin,
  558:       <<"name">> := Name, <<"groups">> := ?DEFAULT_GROUPS} =
  559:         get_ok_value(?GET_CONTACT_PATH, Res).
  560: 
  561: user_get_nonexistent_contact(Config) ->
  562:     escalus:fresh_story_with_config(Config, [{alice, 1}],
  563:                                     fun user_get_nonexistent_contact_story/2).
  564: 
  565: user_get_nonexistent_contact_story(Config, Alice) ->
  566:     Res = user_get_contact(Alice, ?NONEXISTENT_DOMAIN_USER, Config),
  567:     ?assertNotEqual(nomatch, binary:match(get_err_msg(Res), <<"does not exist">>)).
  568: 
  569: % Domain admin test cases
  570: 
  571: domain_admin_try_add_contact_with_unknown_domain(Config) ->
  572:     User = ?NONEXISTENT_DOMAIN_USER,
  573:     Contact = ?NONEXISTENT_USER2,
  574:     Res = admin_add_contact(User, Contact, Config),
  575:     get_unauthorized(Res).
  576: 
  577: domain_admin_try_add_contact_no_permission(Config) ->
  578:     escalus:fresh_story_with_config(Config, [{alice_bis, 1}, {bob, 1}],
  579:                                     fun domain_admin_try_add_contact_no_permission_story/3).
  580: 
  581: domain_admin_try_add_contact_no_permission_story(Config, Alice, Bob) ->
  582:     Res = admin_add_contact(Alice, Bob, Config),
  583:     get_unauthorized(Res).
  584: 
  585: domain_admin_try_delete_contact_with_unknown_domain(Config) ->
  586:     User = ?NONEXISTENT_DOMAIN_USER,
  587:     Res = admin_delete_contact(User, User, Config),
  588:     get_unauthorized(Res).
  589: 
  590: domain_admin_try_delete_contact_no_permission(Config) ->
  591:     escalus:fresh_story_with_config(Config, [{alice_bis, 1}, {bob, 1}],
  592:                                     fun domain_admin_try_delete_contact_no_permission_story/3).
  593: 
  594: domain_admin_try_delete_contact_no_permission_story(Config, Alice, Bob) ->
  595:     Res = admin_delete_contact(Alice, Bob, Config),
  596:     get_unauthorized(Res).
  597: 
  598: domain_admin_set_mutual_subscription_try_connect_nonexistent_users(Config) ->
  599:     Alice = ?NONEXISTENT_DOMAIN_USER,
  600:     Bob = ?NONEXISTENT_USER,
  601:     Res = admin_mutual_subscription(Alice, Bob, <<"CONNECT">>, Config),
  602:     get_unauthorized(Res).
  603: 
  604: domain_admin_set_mutual_subscription_try_connect_users_no_permission(Config) ->
  605:     escalus:fresh_story_with_config(Config, [{alice_bis, 1}, {bob, 1}],
  606:         fun domain_admin_set_mutual_subscription_try_connect_users_no_permission_story/3).
  607: 
  608: domain_admin_set_mutual_subscription_try_connect_users_no_permission_story(Config, Alice, Bob) ->
  609:     Res = admin_mutual_subscription(Alice, Bob, <<"CONNECT">>, Config),
  610:     get_unauthorized(Res).
  611: 
  612: domain_admin_set_mutual_subscription_try_disconnect_nonexistent_users(Config) ->
  613:     Alice = ?NONEXISTENT_DOMAIN_USER,
  614:     Bob = ?NONEXISTENT_USER,
  615:     Res = admin_mutual_subscription(Alice, Bob, <<"DISCONNECT">>, Config),
  616:     get_unauthorized(Res).
  617: 
  618: domain_admin_set_mutual_subscription_try_disconnect_users_no_permission(Config) ->
  619:     escalus:fresh_story_with_config(Config, [{alice_bis, 1}, {bob, 1}],
  620:         fun domain_admin_set_mutual_subscription_try_disconnect_users_no_permission_story/3).
  621: 
  622: domain_admin_set_mutual_subscription_try_disconnect_users_no_permission_story(Config, Alice, Bob) ->
  623:     Res = admin_mutual_subscription(Alice, Bob, <<"DISCONNECT">>, Config),
  624:     get_unauthorized(Res).
  625: 
  626: domain_admin_subscribe_to_all_no_permission(Config) ->
  627:     escalus:fresh_story_with_config(Config, [{alice_bis, 1}],
  628:                                     fun domain_admin_subscribe_to_all_no_permission/2).
  629: 
  630: domain_admin_subscribe_to_all_no_permission(Config, Alice) ->
  631:     get_unauthorized(admin_subscribe_to_all(Alice, [], Config)).
  632: 
  633: domain_admin_subscribe_all_to_all_no_permission(Config) ->
  634:     escalus:fresh_story_with_config(Config, [{alice_bis, 1}, {bob, 1}, {kate, 1}],
  635:         fun domain_admin_subscribe_all_to_all_no_permission/4).
  636: 
  637: domain_admin_subscribe_all_to_all_no_permission(Config, Alice, Bob, Kate) ->
  638:     Res = admin_subscribe_all_to_all([Alice, Bob, Kate], Config),
  639:     get_unauthorized(Res).
  640: 
  641: domain_admin_subscribe_all_to_all_with_wrong_user(Config) ->
  642:     escalus:fresh_story_with_config(Config, [{alice, 1}, {bob, 1}],
  643:         fun domain_admin_subscribe_all_to_all_with_wrong_user_story/3).
  644: 
  645: domain_admin_subscribe_all_to_all_with_wrong_user_story(Config, Alice, Bob) ->
  646:     Kate = ?NONEXISTENT_DOMAIN_USER,
  647:     Res = admin_subscribe_all_to_all([Alice, Bob, Kate], Config),
  648:     get_unauthorized(Res).
  649: 
  650: domain_admin_subscribe_to_all_with_wrong_user(Config) ->
  651:     escalus:fresh_story_with_config(Config, [{alice, 1}, {bob, 1}],
  652:                                     fun domain_admin_subscribe_to_all_with_wrong_user_story/3).
  653: 
  654: domain_admin_subscribe_to_all_with_wrong_user_story(Config, Alice, Bob) ->
  655:     Kate = ?NONEXISTENT_DOMAIN_USER,
  656:     Res = admin_subscribe_to_all(Alice, [Bob, Kate], Config),
  657:     check_if_created_succ(?SUBSCRIBE_TO_ALL_PATH, Res, [true, false]),
  658:     ?assertNotEqual(nomatch, binary:match(get_err_msg(Res), <<"does not exist">>)),
  659:     check_contacts([Bob], Alice),
  660:     check_contacts([Alice], Bob).
  661: 
  662: domain_admin_list_contacts_wrong_user(Config) ->
  663:     % User with a non-existent domain
  664:     Res = admin_list_contacts(?NONEXISTENT_DOMAIN_USER, Config),
  665:     get_unauthorized(Res),
  666:     % Non-existent user with existent domain
  667:     Res2 = admin_list_contacts(?NONEXISTENT_USER, Config),
  668:     ?assertEqual(<<"user_not_exist">>, get_err_code(Res2)).
  669: 
  670: domain_admin_list_contacts_no_permission(Config) ->
  671:     escalus:fresh_story_with_config(Config, [{alice_bis, 1}],
  672:                                     fun domain_admin_list_contacts_no_permission_story/2).
  673: 
  674: domain_admin_list_contacts_no_permission_story(Config, Alice) ->
  675:     Res = admin_list_contacts(Alice, Config),
  676:     get_unauthorized(Res).
  677: 
  678: domain_admin_get_contact_wrong_user(Config) ->
  679:     % User with a non-existent domain
  680:     Res = admin_get_contact(?NONEXISTENT_DOMAIN_USER, ?NONEXISTENT_USER, Config),
  681:     get_unauthorized(Res),
  682:     % Non-existent user with existent domain
  683:     Res2 = admin_get_contact(?NONEXISTENT_USER, ?NONEXISTENT_USER, Config),
  684:     ?assertNotEqual(nomatch, binary:match(get_err_msg(Res2), <<"does not exist">>)).
  685: 
  686: domain_admin_get_contacts_no_permission(Config) ->
  687:     escalus:fresh_story_with_config(Config, [{alice_bis, 1}, {bob, 1}],
  688:                                     fun domain_admin_get_contacts_no_permission_story/3).
  689: 
  690: domain_admin_get_contacts_no_permission_story(Config, Alice, Bob) ->
  691:     Res = admin_get_contact(Alice, Bob, Config),
  692:     get_unauthorized(Res).
  693: 
  694: % Helpers
  695: 
  696: admin_add_contact(User, Contact, Config) ->
  697:     Name = escalus_utils:get_username(Contact),
  698:     admin_add_contact(User, Contact, Name, ?DEFAULT_GROUPS, Config).
  699: 
  700: user_add_contact(User, Contact, Config) ->
  701:     Name = escalus_utils:get_username(Contact),
  702:     user_add_contact(User, Contact, Name, ?DEFAULT_GROUPS, Config).
  703: 
  704: check_contacts(ContactClients, User) ->
  705:     check_contacts(ContactClients, User, ?DEFAULT_GROUPS).
  706: 
  707: check_contacts(ContactClients, User, ContactGroups) ->
  708:     Expected = [escalus_utils:jid_to_lower(escalus_client:short_jid(Client))
  709:                 || Client <- ContactClients],
  710:     ExpectedNames = [escalus_client:username(Client) || Client <- ContactClients],
  711:     ActualContacts = get_roster(User),
  712:     Actual = [ jid:to_binary(JID) || #roster{jid = JID} <- ActualContacts],
  713:     ActualNames = [ Name || #roster{name = Name} <- ActualContacts],
  714:     ?assertEqual(lists:sort(Expected), lists:sort(Actual)),
  715:     ?assertEqual(lists:sort(ExpectedNames), lists:sort(ActualNames)),
  716:     [?assertEqual(ContactGroups, Groups) || #roster{groups = Groups} <- ActualContacts].
  717: 
  718: check_if_created_succ(Path, Res) ->
  719:     OkList = get_ok_value(Path, Res),
  720:     lists:foreach(fun check_created_msg/1, OkList).
  721: 
  722: check_if_created_succ(Path, Res, ExpectedOkValue) ->
  723:     OkList = case lists:member(false, ExpectedOkValue) of
  724:                  true -> get_err_value(Path, Res);
  725:                  false -> get_ok_value(Path, Res)
  726:              end,
  727:     OkList2 = lists:zip(OkList, ExpectedOkValue),
  728:     [check_created_msg(Msg) || {Msg, true} <- OkList2].
  729: 
  730: check_created_msg(Msg) ->
  731:     ?assertNotEqual(nomatch, binary:match(Msg, <<"created successfully">>)).
  732: 
  733: get_roster(User) ->
  734:     {ok, Roster} = rpc(mim(), mod_roster_api, list_contacts, [user_to_jid(User)]),
  735:     Roster.
  736: 
  737: get_roster(User, Contact) ->
  738:     rpc(mim(), mod_roster, get_roster_entry,
  739:         [domain_helper:host_type(),
  740:          user_to_jid(User),
  741:          jid:to_lower(user_to_jid(Contact)),
  742:          full]).
  743: 
  744: make_contacts(Users) ->
  745:     make_contacts(Users, ?DEFAULT_GROUPS).
  746: 
  747: make_contacts(Users, Groups) ->
  748:     [make_contact(U, Groups) || U <- Users].
  749: 
  750: make_contact(U) ->
  751:     make_contact(U, ?DEFAULT_GROUPS).
  752: 
  753: make_contact(U, Groups) ->
  754:     #{jid => user_to_bin(U), name => escalus_utils:get_username(U), groups => Groups}.
  755: 
  756: %% Commands
  757: 
  758: admin_add_contact(User, Contact, Name, Groups, Config) ->
  759:     Vars = #{user => user_to_bin(User), contact => user_to_bin(Contact),
  760:              name => Name, groups => Groups},
  761:     execute_command(<<"roster">>, <<"addContact">>, Vars, Config).
  762: 
  763: admin_add_contacts(User, Contacts, Config) ->
  764:     Vars = #{user => user_to_bin(User), contacts => make_contacts(Contacts)},
  765:     execute_command(<<"roster">>, <<"addContacts">>, Vars, Config).
  766: 
  767: admin_delete_contact(User, Contact, Config) ->
  768:     Vars = #{user => user_to_bin(User), contact => user_to_bin(Contact)},
  769:     execute_command(<<"roster">>, <<"deleteContact">>, Vars, Config).
  770: 
  771: admin_delete_contacts(User, Contacts, Config) ->
  772:     Vars = #{user => user_to_bin(User), contacts => [user_to_bin(C) || C <- Contacts]},
  773:     execute_command(<<"roster">>, <<"deleteContacts">>, Vars, Config).
  774: 
  775: admin_subscription(User, Contact, Action, Config) ->
  776:     Vars = #{user => user_to_bin(User), contact => user_to_bin(Contact), action => Action},
  777:     execute_command(<<"roster">>, <<"subscription">>, Vars, Config).
  778: 
  779: admin_mutual_subscription(User, Contact, Action, Config) ->
  780:     Vars = #{userA => user_to_bin(User), userB => user_to_bin(Contact), action => Action},
  781:     execute_command(<<"roster">>, <<"setMutualSubscription">>, Vars, Config).
  782: 
  783: admin_subscribe_to_all(User, Contacts, Config) ->
  784:     admin_subscribe_to_all(User, Contacts, ?DEFAULT_GROUPS, Config).
  785: 
  786: admin_subscribe_to_all(User, Contacts, Groups, Config) ->
  787:     Vars = #{user => make_contact(User, Groups), contacts => make_contacts(Contacts, Groups)},
  788:     execute_command(<<"roster">>, <<"subscribeToAll">>, Vars, Config).
  789: 
  790: admin_subscribe_to_all_no_args(Config) ->
  791:     execute_command(<<"roster">>, <<"subscribeToAll">>, #{}, Config).
  792: 
  793: admin_subscribe_all_to_all(Users, Config) ->
  794:     admin_subscribe_all_to_all(Users, ?DEFAULT_GROUPS, Config).
  795: 
  796: admin_subscribe_all_to_all(Users, Groups, Config) ->
  797:     Vars = #{contacts => make_contacts(Users, Groups)},
  798:     execute_command(<<"roster">>, <<"subscribeAllToAll">>, Vars, Config).
  799: 
  800: admin_subscribe_all_to_all_no_args(Config) ->
  801:     execute_command(<<"roster">>, <<"subscribeAllToAll">>, #{}, Config).
  802: 
  803: admin_list_contacts(User, Config) ->
  804:     Vars = #{user => user_to_bin(User)},
  805:     execute_command(<<"roster">>, <<"listContacts">>, Vars, Config).
  806: 
  807: admin_get_contact(User, Contact, Config) ->
  808:     Vars = #{user => user_to_bin(User), contact => user_to_bin(Contact)},
  809:     execute_command(<<"roster">>, <<"getContact">>, Vars, Config).
  810: 
  811: user_add_contact(User, Contact, Name, Groups, Config) ->
  812:     Vars = #{contact => user_to_bin(Contact), name => Name, groups => Groups},
  813:     execute_user_command(<<"roster">>, <<"addContact">>, User, Vars, Config).
  814: 
  815: user_add_contacts(User, Contacts, Config) ->
  816:     Vars = #{contacts => make_contacts(Contacts)},
  817:     execute_user_command(<<"roster">>, <<"addContacts">>, User, Vars, Config).
  818: 
  819: user_delete_contact(User, Contact, Config) ->
  820:     Vars = #{contact => user_to_bin(Contact)},
  821:     execute_user_command(<<"roster">>, <<"deleteContact">>, User, Vars, Config).
  822: 
  823: user_delete_contacts(User, Contacts, Config) ->
  824:     Vars = #{contacts => [user_to_bin(C) || C <- Contacts]},
  825:     execute_user_command(<<"roster">>, <<"deleteContacts">>, User, Vars, Config).
  826: 
  827: user_subscription(User, Contact, Action, Config) ->
  828:     Vars = #{contact => user_to_bin(Contact), action => Action},
  829:     execute_user_command(<<"roster">>, <<"subscription">>, User, Vars, Config).
  830: 
  831: user_list_contacts(User, Config) ->
  832:     execute_user_command(<<"roster">>, <<"listContacts">>, User, #{}, Config).
  833: 
  834: user_get_contact(User, Contact, Config) ->
  835:     Vars = #{contact => user_to_bin(Contact)},
  836:     execute_user_command(<<"roster">>, <<"getContact">>, User, Vars, Config).