1: %%==============================================================================
    2: %% Copyright 2010 Erlang Solutions Ltd.
    3: %%
    4: %% Licensed under the Apache License, Version 2.0 (the "License");
    5: %% you may not use this file except in compliance with the License.
    6: %% You may obtain a copy of the License at
    7: %%
    8: %% http://www.apache.org/licenses/LICENSE-2.0
    9: %%
   10: %% Unless required by applicable law or agreed to in writing, software
   11: %% distributed under the License is distributed on an "AS IS" BASIS,
   12: %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   13: %% See the License for the specific language governing permissions and
   14: %% limitations under the License.
   15: %%==============================================================================
   16: 
   17: -module(presence_SUITE).
   18: -compile([export_all, nowarn_export_all]).
   19: 
   20: -include_lib("exml/include/exml.hrl").
   21: -include_lib("escalus/include/escalus.hrl").
   22: -include_lib("common_test/include/ct.hrl").
   23: 
   24: -import(distributed_helper, [mim/0,
   25:                              require_rpc_nodes/1,
   26:                              rpc/4]).
   27: 
   28: -import(domain_helper, [host_type/0]).
   29: 
   30: -import(roster_helper, [set_versioning/3]).
   31: 
   32: %%--------------------------------------------------------------------
   33: %% Suite configuration
   34: %%--------------------------------------------------------------------
   35: 
   36: all() ->
   37:     [{group, presence},
   38:      {group, presence_priority},
   39:      {group, roster},
   40:      {group, roster_versioning},
   41:      {group, subscribe_group}].
   42: 
   43: groups() ->
   44:     [{presence, [parallel], [available,
   45:                              explicit_available,
   46:                              available_direct,
   47:                              available_direct_then_unavailable,
   48:                              become_unavailable,
   49:                              available_direct_then_disconnect,
   50:                              additions,
   51:                              invalid_presence
   52:                             ]},
   53:      {presence_priority, [parallel], [negative_priority_presence]},
   54:      {roster, [parallel], [get_roster,
   55:                            fail_to_get_another_users_roster,
   56:                            add_contact,
   57:                            fail_to_add_contact_for_another_user,
   58:                            remove_contact]},
   59:      {roster_versioning, [], [versioning,
   60:                               versioning_no_store]},
   61:      {subscribe_group, [parallel], [subscribe,
   62:                                     subscribe_decline,
   63:                                     subscribe_relog,
   64:                                     subscribe_preserves_extra_info,
   65:                                     unsubscribe,
   66:                                     remove_unsubscribe]}].
   67: 
   68: suite() ->
   69:     require_rpc_nodes([mim]) ++ escalus:suite().
   70: 
   71: %%--------------------------------------------------------------------
   72: %% Init & teardown
   73: %%--------------------------------------------------------------------
   74: 
   75: init_per_suite(Config) ->
   76:     escalus:init_per_suite(Config).
   77: 
   78: end_per_suite(Config) ->
   79:     escalus_fresh:clean(),
   80:     escalus:end_per_suite(Config).
   81: 
   82: init_per_group(roster_versioning, Config) ->
   83:     Config0 = dynamic_modules:save_modules(host_type(), Config),
   84:     escalus:create_users(Config0, escalus:get_users([alice, bob]));
   85: init_per_group(_GroupName, Config) ->
   86:     escalus:create_users(Config, escalus:get_users([alice, bob])).
   87: 
   88: end_per_group(roster_versioning, Config) ->
   89:     dynamic_modules:restore_modules(Config),
   90:     escalus:delete_users(Config, escalus:get_users([alice, bob]));
   91: end_per_group(_GroupName, Config) ->
   92:     escalus:delete_users(Config, escalus:get_users([alice, bob])).
   93: 
   94: init_per_testcase(versioning, ConfigIn) ->
   95:     Config = set_versioning(true, true, ConfigIn),
   96:     escalus:init_per_testcase(versioning, Config);
   97: init_per_testcase(versioning_no_store, ConfigIn) ->
   98:     Config = set_versioning(true, false, ConfigIn),
   99:     escalus:init_per_testcase(versioning_no_store, Config);
  100: init_per_testcase(CaseName, Config) ->
  101:     escalus:init_per_testcase(CaseName, Config).
  102: 
  103: end_per_testcase(add_contact, Config) ->
  104:     [{_, UserSpec} | _] = escalus_config:get_config(escalus_users, Config),
  105:     remove_roster(Config, UserSpec),
  106:     escalus:end_per_testcase(add_contact, Config);
  107: end_per_testcase(subscribe, Config) ->
  108:     end_rosters_remove(Config);
  109: end_per_testcase(subscribe_decline, Config) ->
  110:     end_rosters_remove(Config);
  111: end_per_testcase(unsubscribe, Config) ->
  112:     end_rosters_remove(Config);
  113: end_per_testcase(VersionCases, Config)
  114:       when VersionCases =:= versioning; VersionCases =:= versioning_no_store ->
  115:     end_rosters_remove(Config);
  116: end_per_testcase(CaseName, Config) ->
  117:     escalus:end_per_testcase(CaseName, Config).
  118: 
  119: end_rosters_remove(Config) ->
  120:     [{_, UserSpec1}, {_, UserSpec2} | _] =
  121:         escalus_config:get_config(escalus_users, Config),
  122:     remove_roster(Config, UserSpec1),
  123:     remove_roster(Config, UserSpec2),
  124:     escalus:end_per_testcase(subscription, Config).
  125: 
  126: 
  127: %%--------------------------------------------------------------------
  128: %% Tests
  129: %%--------------------------------------------------------------------
  130: 
  131: available(Config) ->
  132:     Presence = escalus_stanza:presence(<<"available">>),
  133:     run_send_available_presence(Config, Presence).
  134: 
  135: explicit_available(Config) ->
  136:     Presence = #xmlel{name = <<"presence">>, attrs = [{<<"type">>, <<"available">>}]},
  137:     run_send_available_presence(Config, Presence).
  138: 
  139: run_send_available_presence(Config, Presence) ->
  140:     escalus:fresh_story(Config, [{alice, 1}], fun(Alice) ->
  141:         escalus:send(Alice, Presence),
  142:         escalus:assert(is_presence, escalus:wait_for_stanza(Alice))
  143:     end).
  144: 
  145: available_direct(Config) ->
  146:     escalus:fresh_story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) ->
  147: 
  148:         BobJid = escalus_client:short_jid(Bob),
  149:         escalus:send(Alice, escalus_stanza:presence_direct(BobJid, <<"available">>)),
  150:         Received = escalus:wait_for_stanza(Bob),
  151:         escalus:assert(is_presence, Received),
  152:         escalus_assert:is_stanza_from(Alice, Received)
  153: 
  154:         end).
  155: 
  156: available_direct_then_unavailable(Config) ->
  157:     escalus:fresh_story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) ->
  158:         %% given Alice has sent direct presence to Bob
  159:         escalus:send(Alice, escalus_stanza:presence_direct(Bob, <<"available">>)),
  160:         Received1 = escalus:wait_for_stanza(Bob),
  161:         escalus:assert(is_presence, Received1),
  162:         escalus_assert:is_stanza_from(Alice, Received1),
  163: 
  164:         %% when Alice sends presence unavailable
  165:         escalus:send(Alice, escalus_stanza:presence(<<"unavailable">>)),
  166: 
  167:         %% then Bob receives presence unavailable
  168:         Received2 = escalus:wait_for_stanza(Bob),
  169:         escalus:assert(is_presence, Received2),
  170:         escalus_assert:is_stanza_from(Alice, Received2)
  171:         end).
  172: 
  173: become_unavailable(Config) ->
  174:     %% We test that after sending presence unavailable, priority is set to undefined in the SM table,
  175:     %% so the session is not on a list of active sessions
  176:     escalus:story(Config, [{alice, 1}], fun(Alice) -> push_helper:become_unavailable(Alice) end).
  177: 
  178: available_direct_then_disconnect(Config) ->
  179:     escalus:fresh_story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) ->
  180:         %% given Alice has sent direct presence to Bob
  181:         escalus:send(Alice, escalus_stanza:presence_direct(Bob, <<"available">>)),
  182:         Received1 = escalus:wait_for_stanza(Bob),
  183:         escalus:assert(is_presence, Received1),
  184:         escalus_assert:is_stanza_from(Alice, Received1),
  185: 
  186:         %% when Alice suddenly disconnects
  187:         escalus_client:kill_connection(Config, Alice),
  188: 
  189:         %% then Bob receives presence unavailable
  190:         Received2 = escalus:wait_for_stanza(Bob),
  191:         escalus:assert(is_presence, Received2),
  192:         escalus_assert:is_stanza_from(Alice, Received2)
  193:         end).
  194: 
  195: additions(Config) ->
  196:     escalus:fresh_story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) ->
  197: 
  198:         Tags = escalus_stanza:tags([
  199:             {<<"show">>, <<"dnd">>},
  200:             {<<"priority">>, <<"1">>},
  201:             {<<"status">>, <<"Short break">>}
  202:         ]),
  203:         BobJid = escalus_client:short_jid(Bob),
  204:         Presence = escalus_stanza:presence_direct(BobJid, <<"available">>, Tags),
  205:         escalus:send(Alice, Presence),
  206: 
  207:         Received = escalus:wait_for_stanza(Bob),
  208:         escalus:assert(is_presence, Received),
  209:         escalus:assert(is_presence_with_show, [<<"dnd">>], Received),
  210:         escalus:assert(is_presence_with_status, [<<"Short break">>], Received),
  211:         escalus:assert(is_presence_with_priority, [<<"1">>], Received)
  212: 
  213:         end).
  214: 
  215: invalid_presence(Config) ->
  216:     escalus:fresh_story(Config, [{alice, 1}], fun(Alice) ->
  217:         Presence = escalus_stanza:presence(<<"invalid-type">>),
  218:         escalus:send(Alice, Presence),
  219:         Received = escalus:wait_for_stanza(Alice),
  220:         escalus_assert:is_error(Received, <<"modify">>, <<"bad-request">>)
  221:     end).
  222: 
  223: negative_priority_presence(Config) ->
  224:     escalus:fresh_story(Config, [{alice, 2}, {bob, 1}], fun(Alice1, Alice2, Bob) ->
  225: 
  226:         %% Alice1 updates presense priority
  227:         Tags = escalus_stanza:tags([
  228:             {<<"priority">>, <<"-10">>}
  229:         ]),
  230:         Presence = escalus_stanza:presence(<<"available">>, Tags),
  231:         escalus:send(Alice1, Presence),
  232: 
  233:         Received1 = escalus:wait_for_stanza(Alice1),
  234:         Received2 = escalus:wait_for_stanza(Alice2),
  235:         escalus:assert(is_presence, Received1),
  236:         escalus:assert(is_presence, Received2),
  237:         escalus:assert(is_presence_with_priority, [<<"-10">>], Received1),
  238:         escalus:assert(is_presence_with_priority, [<<"-10">>], Received2),
  239: 
  240:         %% Bob sends to the Alice's bare JID.
  241:         escalus:send(Bob, escalus_stanza:chat_to_short_jid(Alice1, <<"Hi.">>)),
  242: 
  243:         %% If priority is negative, than the client does not want to receive
  244:         %% any messages.
  245:         timer:sleep(1000),
  246:         escalus_assert:has_no_stanzas(Alice1)
  247: 
  248:         end).
  249: 
  250: get_roster(Config) ->
  251:     escalus:fresh_story(Config, [{alice, 1}], fun(Alice) ->
  252:         escalus:send(Alice, escalus_stanza:roster_get()),
  253:         escalus_assert:is_roster_result(escalus:wait_for_stanza(Alice))
  254: 
  255:         end).
  256: 
  257: fail_to_get_another_users_roster(Config) ->
  258:     escalus:fresh_story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) ->
  259:         BobJid = escalus_client:short_jid(Bob),
  260:         Request = escalus_stanza:roster_get(),
  261:         escalus:send(Alice, escalus_stanza:to(Request, BobJid)),
  262:         Response = escalus:wait_for_stanza(Alice),
  263:         escalus:assert(is_iq_error, [Request], Response),
  264:         escalus:assert(is_error, [<<"auth">>, <<"forbidden">>], Response),
  265:         escalus:assert(is_stanza_from, [BobJid], Response)
  266: 
  267:         end).
  268: 
  269: add_contact(Config) ->
  270:     escalus:fresh_story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) ->
  271: 
  272:         %% add contact
  273:         Stanza = escalus_stanza:roster_add_contact(Bob, bobs_default_groups(),
  274:                                                    bobs_default_name()),
  275:         escalus:send(Alice, Stanza),
  276:         Received = escalus:wait_for_stanzas(Alice, 2),
  277:         escalus:assert_many([is_roster_set, is_iq_result], Received),
  278: 
  279:         Result = hd([R || R <- Received, escalus_pred:is_roster_set(R)]),
  280:         escalus:assert(count_roster_items, [1], Result),
  281:         escalus:send(Alice, escalus_stanza:iq_result(Result)),
  282: 
  283:         %% check roster
  284:         escalus:send(Alice, escalus_stanza:roster_get()),
  285:         Received2 = escalus:wait_for_stanza(Alice),
  286: 
  287:         escalus:assert(is_roster_result, Received2),
  288:         BobJid = escalus_client:short_jid(Bob),
  289:         escalus:assert(roster_contains, [BobJid], Received2)
  290: 
  291:         end).
  292: 
  293: fail_to_add_contact_for_another_user(Config) ->
  294:     escalus:story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) ->
  295:         BobJid = escalus_client:short_jid(Bob),
  296:         Request = escalus_stanza:roster_add_contact(BobJid, bobs_default_groups(),
  297:                                                     bobs_default_name()),
  298:         escalus:send(Alice, escalus_stanza:to(Request, BobJid)),
  299:         Response = escalus:wait_for_stanza(Alice),
  300:         escalus:assert(is_iq_error, [Request], Response),
  301:         escalus:assert(is_error, [<<"auth">>, <<"forbidden">>], Response),
  302:         escalus:assert(is_stanza_from, [BobJid], Response)
  303: 
  304:         end).
  305: 
  306: remove_contact(Config) ->
  307:     escalus:fresh_story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) ->
  308: 
  309:         %% add contact
  310:         add_sample_contact(Alice, Bob),
  311: 
  312:         %% check roster
  313:         escalus:send(Alice, escalus_stanza:roster_get()),
  314:         escalus:assert(count_roster_items, [1], escalus:wait_for_stanza(Alice)),
  315: 
  316:         %% remove contact
  317:         escalus:send(Alice, escalus_stanza:roster_remove_contact(Bob)),
  318:         IsSubscriptionRemove = fun(El) ->
  319:             Sub = exml_query:paths(El, [{element, <<"query">>},
  320:                                   {element, <<"item">>},
  321:                                   {attr, <<"subscription">>}]),
  322:             Sub == [<<"remove">>]
  323:             end,
  324:         escalus:assert_many([IsSubscriptionRemove, is_iq_result],
  325:                             escalus:wait_for_stanzas(Alice, 2)),
  326: 
  327:         %% check roster
  328:         escalus:send(Alice, escalus_stanza:roster_get()),
  329:         escalus:assert(count_roster_items, [0], escalus:wait_for_stanza(Alice))
  330: 
  331:     end).
  332: 
  333: versioning(Config) ->
  334:     escalus:fresh_story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) ->
  335: 
  336:         escalus:send(Alice, escalus_stanza:roster_get(<<"">>)),
  337:         RosterResult = escalus:wait_for_stanza(Alice),
  338: 
  339:         escalus_assert:is_roster_result(RosterResult),
  340:         Ver = exml_query:path(RosterResult, [{element, <<"query">>}, {attr, <<"ver">>}]),
  341: 
  342:         true = Ver /= undefined,
  343: 
  344:         %% add contact
  345:         Stanza = escalus_stanza:roster_add_contact(Bob, bobs_default_groups(),
  346:                                                    bobs_default_name()),
  347:         escalus:send(Alice, Stanza),
  348:         Received = escalus:wait_for_stanzas(Alice, 2),
  349: 
  350:         escalus:assert_many([is_roster_set, is_iq_result], Received),
  351: 
  352:         RosterSet = hd(Received),
  353: 
  354:         Ver2 = exml_query:path(RosterSet, [{element, <<"query">>}, {attr, <<"ver">>}]),
  355: 
  356:         true = Ver2 /= undefined,
  357: 
  358:         Result = hd([R || R <- Received, escalus_pred:is_roster_set(R)]),
  359:         escalus:assert(count_roster_items, [1], Result),
  360:         escalus:send(Alice, escalus_stanza:iq_result(Result)),
  361: 
  362:         %% check roster, send old ver
  363:         escalus:send(Alice, escalus_stanza:roster_get(Ver)),
  364:         Received2 = escalus:wait_for_stanza(Alice),
  365: 
  366:         escalus:assert(is_roster_result, Received2),
  367:         BobJid = escalus_client:short_jid(Bob),
  368:         escalus:assert(roster_contains, [BobJid], Received2),
  369: 
  370:         %% check version
  371: 
  372:         Ver2 = exml_query:path(Received2, [{element, <<"query">>}, {attr, <<"ver">>}]),
  373: 
  374:         %% check roster, send correct Ver
  375: 
  376:         escalus:send(Alice, escalus_stanza:roster_get(Ver2)),
  377:         Received3 = escalus:wait_for_stanza(Alice),
  378: 
  379:         escalus:assert(is_iq_result, Received3),
  380: 
  381:         %% There is no content as version matches
  382: 
  383:         undefined = exml_query:path(Received3, [{element, <<"query">>}])
  384: 
  385:     end).
  386: 
  387: versioning_no_store(Config) ->
  388:     versioning(Config).
  389: 
  390: subscribe(Config) ->
  391:     escalus:fresh_story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) ->
  392:         BobJid = escalus_client:short_jid(Bob),
  393:         AliceJid = escalus_client:short_jid(Alice),
  394: 
  395:         %% Alice adds Bob as a contact
  396:         add_sample_contact(Alice, Bob),
  397: 
  398:         %% She subscribes to his presences
  399:         escalus:send(Alice, escalus_stanza:presence_direct(BobJid,
  400:                      <<"subscribe">>)),
  401:         PushReq = escalus:wait_for_stanza(Alice),
  402:         escalus:assert(is_roster_set, PushReq),
  403:         escalus:send(Alice, escalus_stanza:iq_result(PushReq)),
  404: 
  405:         %% Bob receives subscription request
  406:         Received = escalus:wait_for_stanza(Bob),
  407:         escalus:assert(is_presence_with_type, [<<"subscribe">>], Received),
  408: 
  409:         check_roster_count(Bob, 0), % she is in his roster but invisible
  410:         % because she is {none, in}
  411: 
  412:         %% Bob adds new contact to his roster
  413:         escalus:send(Bob, escalus_stanza:roster_add_contact(Alice,
  414:                                                             [<<"enemies">>],
  415:                                                              <<"Alice">>)),
  416:         PushReqB = escalus:wait_for_stanza(Bob),
  417:         escalus:assert(is_roster_set, PushReqB),
  418:         escalus:send(Bob, escalus_stanza:iq_result(PushReqB)),
  419:         escalus:assert(is_iq_result, escalus:wait_for_stanza(Bob)),
  420: 
  421:         %% Bob sends subscribed presence
  422:         escalus:send(Bob, escalus_stanza:presence_direct(AliceJid,
  423:                      <<"subscribed">>)),
  424: 
  425:         %% Alice receives subscribed
  426:         Stanzas = escalus:wait_for_stanzas(Alice, 2),
  427: 
  428:         check_subscription_stanzas(Stanzas, <<"subscribed">>),
  429:         escalus:assert(is_presence, escalus:wait_for_stanza(Alice)),
  430: 
  431:         %% Bob receives roster push
  432:         PushReqB1 = escalus:wait_for_stanza(Bob),
  433:         escalus:assert(is_roster_set, PushReqB1),
  434: 
  435:         check_roster_count(Bob, 1),
  436:         %% Bob sends presence
  437:         escalus:send(Bob, escalus_stanza:presence(<<"available">>)),
  438:         escalus:assert(is_presence, escalus:wait_for_stanza(Alice)),
  439: 
  440:         %% Bob sends presence
  441:         escalus:send(Bob, escalus_stanza:presence(<<"unavailable">>)),
  442:         escalus:assert(is_presence_with_type, [<<"unavailable">>],
  443:                        escalus:wait_for_stanza(Alice))
  444: 
  445:         end).
  446: 
  447: subscribe_decline(Config) ->
  448:     escalus:fresh_story(Config, [{alice, 1}, {bob, 1}], fun(Alice,Bob) ->
  449:         BobJid = escalus_client:short_jid(Bob),
  450:         AliceJid = escalus_client:short_jid(Alice),
  451: 
  452:         %% add contact
  453:         add_sample_contact(Alice, Bob),
  454: 
  455:         %% subscribe
  456:         escalus:send(Alice, escalus_stanza:presence_direct(BobJid, <<"subscribe">>)),
  457:         PushReq = escalus:wait_for_stanza(Alice),
  458:         escalus_assert:is_roster_set(PushReq),
  459:         escalus:send(Alice, escalus_stanza:iq_result(PushReq)),
  460: 
  461:         %% Bob receives subscription request
  462:         Received = escalus:wait_for_stanza(Bob),
  463:         escalus:assert(is_presence_with_type, [<<"subscribe">>], Received),
  464: 
  465:         %% Bob refuses subscription
  466:         escalus:send(Bob, escalus_stanza:presence_direct(AliceJid, <<"unsubscribed">>)),
  467: 
  468:         %% Alice receives subscribed
  469:         Stanzas = escalus:wait_for_stanzas(Alice, 2),
  470: 
  471:         check_subscription_stanzas(Stanzas, <<"unsubscribed">>)
  472: 
  473:     end).
  474: 
  475: subscribe_relog(Config) ->
  476:     Users = [{alice, 1}, {bob, 1}],
  477:     FreshConfig = escalus_fresh:create_users(Config, Users),
  478:     escalus:story(FreshConfig, Users, fun(Alice, Bob) ->
  479:         BobJid = escalus_client:short_jid(Bob),
  480:         AliceJid = escalus_client:short_jid(Alice),
  481: 
  482:         %% Alice adds Bob as a contact
  483:         add_sample_contact(Alice, Bob),
  484: 
  485:         %% She subscribes to his presences
  486:         escalus:send(Alice, escalus_stanza:presence_direct(BobJid, <<"subscribe">>)),
  487:         PushReq = escalus:wait_for_stanza(Alice),
  488:         escalus:assert(is_roster_set, PushReq),
  489:         escalus:send(Alice, escalus_stanza:iq_result(PushReq)),
  490: 
  491:         %% Bob receives subscription request
  492:         Received = escalus:wait_for_stanza(Bob),
  493:         escalus:assert(is_presence_with_type, [<<"subscribe">>], Received),
  494: 
  495:         %% New Bob resource connects, should receive subscription request again
  496:         {ok, NewBob} = escalus_client:start_for(FreshConfig, bob, <<"newbob">>),
  497:         escalus:send(NewBob, escalus_stanza:presence(<<"available">>)),
  498: 
  499:         escalus:assert(is_presence_with_type, [<<"available">>],
  500:                        escalus:wait_for_stanza(Bob)),
  501: 
  502:         Stanzas = escalus:wait_for_stanzas(NewBob, 3),
  503:         3 = length(Stanzas),
  504: 
  505:         escalus_new_assert:mix_match([
  506:                 fun(S) ->
  507:                     escalus_pred:is_presence_with_type(<<"available">>, S)
  508:                     andalso escalus_pred:is_stanza_from(Bob, S)
  509:                 end,
  510:                 fun(S) ->
  511:                     escalus_pred:is_presence_with_type(<<"available">>, S)
  512:                     andalso escalus_pred:is_stanza_from(NewBob, S)
  513:                 end,
  514:                 fun(S) ->
  515:                     escalus_pred:is_presence_with_type(<<"subscribe">>, S)
  516:                     andalso escalus_pred:is_stanza_from(AliceJid, S)
  517:                 end
  518:             ], Stanzas),
  519: 
  520:         escalus_client:stop(Config, NewBob),
  521: 
  522:         escalus:send(Bob, escalus_stanza:presence_direct(AliceJid, <<"unsubscribed">>))
  523: 
  524:         end).
  525: 
  526: %% This test verifies that a subscription request doesn't remove nickname of a contact
  527: %% and doesn't remove them from a group.
  528: subscribe_preserves_extra_info(Config) ->
  529:     escalus:fresh_story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) ->
  530:         %% Alice adds Bob as a contact
  531:         add_sample_contact(Alice, Bob),
  532: 
  533:         %% Subscription without confirmation to prevent unavailable presence exchange
  534:         %% after the test; TODO: escalus_story should ignore them automatically
  535:         BobJid = escalus_client:short_jid(Bob),
  536:         escalus:send(Alice, escalus_stanza:presence_direct(BobJid, <<"subscribe">>)),
  537:         PushReq = escalus:wait_for_stanza(Alice), % Roster set
  538:         escalus:send(Alice, escalus_stanza:iq_result(PushReq)),
  539:         Received = escalus:wait_for_stanza(Bob),
  540:         escalus:assert(is_presence_with_type, [<<"subscribe">>], Received),
  541: 
  542:         %% Alice gets current roster
  543:         escalus:send(Alice, escalus_stanza:roster_get()),
  544:         RosterResult = escalus:wait_for_stanza(Alice),
  545:         escalus_assert:is_roster_result(RosterResult),
  546: 
  547:         %% Actual verification
  548:         [BobItem] = exml_query:paths(RosterResult, [{element, <<"query">>}, {element, <<"item">>}]),
  549: 
  550:         ValidName = bobs_default_name(),
  551:         ValidGroups = lists:sort(bobs_default_groups()),
  552: 
  553:         {ValidName, ValidGroups}
  554:         = {exml_query:attr(BobItem, <<"name">>),
  555:            lists:sort(exml_query:paths(BobItem, [{element, <<"group">>}, cdata]))}
  556:         end).
  557: 
  558: unsubscribe(Config) ->
  559:     escalus:fresh_story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) ->
  560:         BobJid = escalus_client:short_jid(Bob),
  561:         AliceJid = escalus_client:short_jid(Alice),
  562: 
  563:         %% add contact
  564:         add_sample_contact(Alice, Bob),
  565: 
  566:         %% subscribe
  567:         escalus:send(Alice, escalus_stanza:presence_direct(BobJid, <<"subscribe">>)),
  568:         PushReq = escalus:wait_for_stanza(Alice),
  569:         escalus:send(Alice, escalus_stanza:iq_result(PushReq)),
  570: 
  571:         %% Bob receives subscription request
  572:         escalus:assert(is_presence_with_type, [<<"subscribe">>],
  573:                        escalus:wait_for_stanza(Bob)),
  574:         %% Bob adds new contact to his roster
  575:         escalus:send(Bob, escalus_stanza:roster_add_contact(Alice,
  576:                                                             [<<"enemies">>],
  577:                                                              <<"Alice">>)),
  578:         PushReqB = escalus:wait_for_stanza(Bob),
  579:         escalus:send(Bob, escalus_stanza:iq_result(PushReqB)),
  580:         escalus:assert(is_iq_result, escalus:wait_for_stanza(Bob)),
  581: 
  582:         %% Bob sends subscribed presence
  583:         escalus:send(Bob, escalus_stanza:presence_direct(AliceJid, <<"subscribed">>)),
  584: 
  585:         %% Alice receives subscribed
  586:         Stanzas = escalus:wait_for_stanzas(Alice, 2),
  587: 
  588:         check_subscription_stanzas(Stanzas, <<"subscribed">>),
  589:         escalus:assert(is_presence, escalus:wait_for_stanza(Alice)),
  590: 
  591:         %% Bob receives roster push
  592:         PushReqB1 = escalus:wait_for_stanza(Bob),
  593:         escalus_assert:is_roster_set(PushReqB1),
  594: 
  595:         %% Alice sends unsubscribe
  596:         escalus:send(Alice, escalus_stanza:presence_direct(BobJid, <<"unsubscribe">>)),
  597: 
  598:         PushReqA2 = escalus:wait_for_stanza(Alice),
  599:         escalus_assert:is_roster_set(PushReqA2),
  600:         escalus:send(Alice, escalus_stanza:iq_result(PushReqA2)),
  601: 
  602:         %% Bob receives unsubscribe
  603: 
  604:         StanzasB = escalus:wait_for_stanzas(Bob, 2),
  605: 
  606:         check_subscription_stanzas(StanzasB, <<"unsubscribe">>),
  607: 
  608:         %% Alice receives unsubscribed
  609:         escalus:assert(is_presence_with_type, [<<"unavailable">>],
  610:                        escalus:wait_for_stanza(Alice))
  611:     end).
  612: 
  613: remove_unsubscribe(Config) ->
  614:     escalus:fresh_story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) ->
  615:         BobJid = escalus_client:short_jid(Bob),
  616:         AliceJid = escalus_client:short_jid(Alice),
  617: 
  618:         %% add contact
  619:         add_sample_contact(Alice, Bob),
  620: 
  621:         %% subscribe
  622:         escalus:send(Alice, escalus_stanza:presence_direct(BobJid, <<"subscribe">>)),
  623:         PushReq = escalus:wait_for_stanza(Alice),
  624:         escalus:send(Alice, escalus_stanza:iq_result(PushReq)),
  625: 
  626:         %% Bob receives subscription request
  627:         escalus:assert(is_presence_with_type, [<<"subscribe">>],
  628:                        escalus:wait_for_stanza(Bob)),
  629:         %% Bob adds new contact to his roster
  630:         escalus:send(Bob, escalus_stanza:roster_add_contact(Alice,
  631:                                                             [<<"enemies">>],
  632:                                                             <<"Alice">>)),
  633:         PushReqB = escalus:wait_for_stanza(Bob),
  634:         escalus:send(Bob, escalus_stanza:iq_result(PushReqB)),
  635:         escalus:assert(is_iq_result, escalus:wait_for_stanza(Bob)),
  636: 
  637:         %% Bob sends subscribed presence
  638:         escalus:send(Bob, escalus_stanza:presence_direct(AliceJid, <<"subscribed">>)),
  639: 
  640:         %% Alice receives subscribed
  641:         Stanzas = [escalus:wait_for_stanza(Alice),
  642:                    escalus:wait_for_stanza(Alice)],
  643: 
  644:         check_subscription_stanzas(Stanzas, <<"subscribed">>),
  645:         escalus:assert(is_presence, escalus:wait_for_stanza(Alice)),
  646: 
  647:         %% Bob receives roster push
  648:         PushReqB1 = escalus:wait_for_stanza(Bob),
  649:         escalus_assert:is_roster_set(PushReqB1),
  650: 
  651:         %% remove contact
  652:         escalus:send(Alice, escalus_stanza:roster_remove_contact(Bob)),
  653: 
  654:         IsPresUnavailable =
  655:                 fun(S) ->
  656:                     escalus_pred:is_presence_with_type(<<"unavailable">>, S)
  657:                 end,
  658:         escalus:assert_many([is_roster_set, is_iq_result, IsPresUnavailable],
  659:                             escalus:wait_for_stanzas(Alice, 3)),
  660:         check_subscription_stanzas(escalus:wait_for_stanzas(Bob, 2),
  661:                                    <<"unsubscribe">>)
  662: 
  663:     end).
  664: 
  665: 
  666: %%-----------------------------------------------------------------
  667: %% Helpers
  668: %%-----------------------------------------------------------------
  669: 
  670: bobs_default_groups() -> [<<"friends">>].
  671: 
  672: bobs_default_name() -> <<"Bobby">>.
  673: 
  674: add_sample_contact(Alice, Bob) ->
  675:     escalus:send(Alice, escalus_stanza:roster_add_contact(Bob,
  676:                                                           bobs_default_groups(),
  677:                                                           bobs_default_name())),
  678: 
  679:     Received = escalus:wait_for_stanzas(Alice, 2),
  680:     escalus:assert_many([is_roster_set, is_iq_result], Received),
  681: 
  682:     Result = hd([R || R <- Received, escalus_pred:is_roster_set(R)]),
  683:     escalus:assert(count_roster_items, [1], Result),
  684:     escalus:send(Alice, escalus_stanza:iq_result(Result)).
  685: 
  686: check_subscription_stanzas(Stanzas, Type) ->
  687:     IsPresWithType = fun(S) ->
  688:                          escalus_pred:is_presence_with_type(Type, S)
  689:                      end,
  690:     escalus:assert_many([is_roster_set, IsPresWithType], Stanzas).
  691: 
  692: remove_roster(Config, UserSpec) ->
  693:     [Username, Server, _Pass] = [escalus_ejabberd:unify_str_arg(Item) ||
  694:                                  Item <- escalus_users:get_usp(Config, UserSpec)],
  695:     Mods = rpc(mim(), gen_mod, loaded_modules, [host_type()]),
  696:     case lists:member(mod_roster, Mods) of
  697:         true ->
  698:             Acc = mongoose_helper:new_mongoose_acc(Server),
  699:             Extra = #{host_type => host_type()},
  700:             Params = #{jid => jid:make_bare(Username, Server)},
  701:             rpc(mim(), mod_roster, remove_user, [Acc, Params, Extra]);
  702:         false ->
  703:             case lists:member(mod_roster_rdbms, Mods) of
  704:                 true ->
  705:                     rpc(mim(), mod_roster_rdbms, remove_user_t, [host_type(), Username, Server]);
  706:                 false ->
  707:                     throw(roster_not_loaded)
  708:             end
  709:     end.
  710: 
  711: check_roster_count(User, ExpectedCount) ->
  712:     % the user sends get_roster iq
  713:     escalus_client:send(User, escalus_stanza:roster_get()),
  714:     Roster = escalus_client:wait_for_stanza(User),
  715:     ct:pal("Roster: ~p", [Roster]),
  716:     % Roster contains all created users excluding user
  717:     escalus:assert(is_roster_result, Roster),
  718:     escalus:assert(count_roster_items, [ExpectedCount], Roster).