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