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