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(mod_blocking_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: -include_lib("eunit/include/eunit.hrl").
   24: -include_lib("escalus/include/escalus_xmlns.hrl").
   25: 
   26: -define(SLEEP_TIME, 50).
   27: 
   28: %%--------------------------------------------------------------------
   29: %% Suite configuration
   30: %%--------------------------------------------------------------------
   31: 
   32: all() ->
   33:     [
   34:         {group, manage},
   35:         {group, effect},
   36:         {group, offline},
   37:         {group, errors},
   38:         {group, pushes}
   39:     ].
   40: 
   41: groups() ->
   42:     G = [
   43:          {manage, [parallel], manage_test_cases()},
   44:          {effect, [parallel], effect_test_cases()},
   45:          {offline, [sequence], offline_test_cases()},
   46:          {errors, [parallel], error_test_cases()},
   47:          {pushes, [parallel], push_test_cases()},
   48:          {notify, [parallel], notify_test_cases()}
   49:         ],
   50:     ct_helper:repeat_all_until_all_ok(G).
   51: 
   52: manage_test_cases() ->
   53:     [
   54:         discovering_support,
   55:         get_block_list,
   56:         add_user_to_blocklist,
   57:         add_user_to_blocklist_with_white_spaces,
   58:         add_another_user_to_blocklist,
   59:         add_many_users_to_blocklist,
   60:         remove_user_from_blocklist,
   61:         remove_many_user_from_blocklist,
   62:         clear_blocklist,
   63:         invalid_block_request
   64:     ].
   65: 
   66: effect_test_cases() ->
   67:     [
   68:         messages_from_blocked_user_dont_arrive,
   69:         messages_from_unblocked_user_arrive_again,
   70:         messages_from_any_blocked_resource_dont_arrive,
   71:         blocking_doesnt_interfere,
   72:         blocking_propagates_to_resources,
   73:         iq_reply_doesnt_crash_user_process
   74:     ].
   75: 
   76: offline_test_cases() ->
   77:     [
   78:         messages_after_relogin,
   79:         messages_arrive_after_unblock_and_relogin,
   80:         blocking_and_relogin_many,
   81:         clear_list_relogin
   82:     ].
   83: 
   84: error_test_cases() ->
   85:     [blocker_cant_send_to_blockee].
   86: 
   87: push_test_cases() ->
   88:     [block_push_sent].
   89: 
   90: notify_test_cases() ->
   91:     [notify_blockee].
   92: 
   93: suite() ->
   94:     escalus:suite().
   95: 
   96: %%--------------------------------------------------------------------
   97: %% Init & teardown
   98: %%--------------------------------------------------------------------
   99: 
  100: init_per_suite(Config0) ->
  101:     HostType = domain_helper:host_type(),
  102:     Config1 = dynamic_modules:save_modules(HostType, Config0),
  103:     Backend = mongoose_helper:get_backend_mnesia_rdbms_riak(HostType),
  104:     ModConfig = [{mod_blocking, set_opts(Backend)}],
  105:     dynamic_modules:ensure_modules(HostType, ModConfig),
  106:     [{backend, Backend} |
  107:      escalus:init_per_suite(Config1)].
  108: 
  109: set_opts(riak) ->
  110:     #{riak => config_parser_helper:config([modules, mod_privacy, riak], #{}), backend => riak};
  111: set_opts(Backend) ->
  112:     #{backend => Backend}.
  113: 
  114: end_per_suite(Config) ->
  115:     escalus_fresh:clean(),
  116:     dynamic_modules:restore_modules(Config),
  117:     escalus:end_per_suite(Config).
  118: 
  119: init_per_group(_GroupName, Config) ->
  120:     escalus_fresh:create_users(Config, escalus:get_users([alice, bob, kate, mike, john])).
  121: 
  122: end_per_group(_GroupName, Config) ->
  123:     Config.
  124: 
  125: init_per_testcase(CaseName, Config) ->
  126:     escalus:init_per_testcase(CaseName, Config).
  127: 
  128: end_per_testcase(CaseName, Config) ->
  129:     escalus:end_per_testcase(CaseName, Config).
  130: 
  131: %%--------------------------------------------------------------------
  132: %% Tests
  133: %%--------------------------------------------------------------------
  134: 
  135: discovering_support(Config) ->
  136:     escalus:fresh_story(
  137:         Config, [{alice, 1}],
  138:         fun(User1) ->
  139:             Server = escalus_client:server(User1),
  140:             IqGet = escalus_stanza:disco_info(Server),
  141:             escalus_client:send(User1, IqGet),
  142:             Result = escalus_client:wait_for_stanza(User1),
  143:             escalus:assert(is_iq_result, [IqGet], Result),
  144:             escalus:assert(has_feature, [?NS_BLOCKING], Result)
  145:         end).
  146: 
  147: 
  148: get_block_list(Config) ->
  149:     escalus:fresh_story(
  150:         Config, [{alice, 1}],
  151:         fun(User1) ->
  152:             Result = get_blocklist(User1),
  153:             escalus:assert(is_iq_result, Result),
  154:             escalus:assert(fun is_blocklist_result_empty/1, Result)
  155:         end).
  156: 
  157: 
  158: add_user_to_blocklist(Config) ->
  159:     escalus:fresh_story(
  160:         Config, [{alice, 1}, {bob, 1}],
  161:         fun(User1, User2) ->
  162:             user_blocks(User1, [User2]),
  163:             BlockList = get_blocklist(User1),
  164:             blocklist_contains_jid(BlockList, User2)
  165:         end).
  166: 
  167: add_user_to_blocklist_with_white_spaces(Config) ->
  168:     escalus:fresh_story(
  169:         Config, [{alice, 1}, {bob, 1}],
  170:         fun(User1, User2) ->
  171:             BlockeeJIDs = [escalus_utils:jid_to_lower(escalus_client:short_jid(B)) || B <- [User2]],
  172:             AddStanza = block_users_stanza_with_white_spaces(BlockeeJIDs),
  173:             escalus_client:send(User1, AddStanza),
  174:             Res = escalus:wait_for_stanza(User1),
  175:             escalus:assert(is_iq_result, Res)
  176:         end).
  177: 
  178: add_another_user_to_blocklist(Config) ->
  179:     escalus:fresh_story(
  180:         Config, [{alice, 1}, {mike, 1}],
  181:         fun(User1, User2) ->
  182:             user_blocks(User1, [User2]),
  183:             BlockList = get_blocklist(User1),
  184:             blocklist_contains_jid(BlockList, User2)
  185:         end).
  186: 
  187: add_many_users_to_blocklist(Config) ->
  188:     escalus:fresh_story(
  189:         Config, [{alice, 1}, {bob, 1}, {kate, 1}, {mike, 1}],
  190:         fun(User1, User2, User3, User4) ->
  191:             user_blocks(User1, [User2, User3, User4]),
  192:             BlockList = get_blocklist(User1),
  193:             blocklist_contains_jid(BlockList, User2),
  194:             blocklist_contains_jid(BlockList, User3),
  195:             blocklist_contains_jid(BlockList, User4)
  196:         end).
  197: 
  198: remove_user_from_blocklist(Config) ->
  199:     escalus:fresh_story(
  200:         Config, [{alice, 1}, {bob, 1}],
  201:         fun(User1, User2) ->
  202:             user_blocks(User1, [User2]),
  203:             user_unblocks(User1, User2),
  204:             NewList = get_blocklist(User1),
  205:             blocklist_doesnt_contain_jid(NewList, User2)
  206:         end).
  207: 
  208: remove_many_user_from_blocklist(Config) ->
  209:     escalus:fresh_story(
  210:         Config, [{alice, 1}, {bob, 1}, {kate, 1}],
  211:         fun(User1, User2, User3) ->
  212:             user_blocks(User1, [User2, User3]),
  213:             user_unblocks(User1, [User2, User3]),
  214:             NewList = get_blocklist(User1),
  215:             blocklist_doesnt_contain_jid(NewList, User2),
  216:             blocklist_doesnt_contain_jid(NewList, User3)
  217:         end).
  218: 
  219: clear_blocklist(Config) ->
  220:     escalus:fresh_story(
  221:         Config, [{alice, 1}, {bob, 1}, {kate, 1}],
  222:         fun(User1, User2, User3) ->
  223:             user_blocks(User1, [User2, User3]),
  224:             user_unblocks_all(User1),
  225:             NewList = get_blocklist(User1),
  226:             blocklist_is_empty(NewList)
  227:         end).
  228: 
  229: invalid_block_request(Config) ->
  230:     escalus:fresh_story(
  231:         Config, [{alice, 1}],
  232:         fun(User1) ->
  233:             St = block_users_stanza([]),
  234:             escalus_client:send(User1, St),
  235:             privacy_helper:gets_error(User1, <<"modify">>, <<"bad-request">>)
  236:         end).
  237: 
  238: messages_from_blocked_user_dont_arrive(Config) ->
  239:     escalus:fresh_story(
  240:         Config, [{alice, 1}, {bob, 1}],
  241:         fun(User1, User2) ->
  242:             user_blocks(User1, [User2]),
  243:             message(User2, User1, <<"Hi!">>),
  244:             ct:sleep(100),
  245:             escalus_assert:has_no_stanzas(User1),
  246:             privacy_helper:gets_error(User2, <<"cancel">>, <<"service-unavailable">>)
  247:         end).
  248: 
  249: messages_from_unblocked_user_arrive_again(Config) ->
  250:     escalus:fresh_story(
  251:         Config, [{alice, 1}, {bob, 1}],
  252:         fun(User1, User2) ->
  253:             %% given
  254:             user_blocks(User1, [User2]),
  255:             %% when
  256:             user_unblocks(User1, User2),
  257:             %% then
  258:             message_is_delivered(User2, User1, <<"Hello again!">>)
  259:         end).
  260: 
  261: messages_from_any_blocked_resource_dont_arrive(Config) ->
  262:     escalus:fresh_story(
  263:         Config, [{alice, 3}, {bob, 1}],
  264:         fun(User1a, User1b, User1c, User2) ->
  265:             %% given
  266:             user_blocks(User2, [User1a]),
  267:             %% then
  268:             message_is_not_delivered(User1a, [User2], <<"roar!">>),
  269:             privacy_helper:gets_error(User1a, <<"cancel">>, <<"service-unavailable">>),
  270:             message_is_not_delivered(User1b, [User2], <<"woof!">>),
  271:             privacy_helper:gets_error(User1b, <<"cancel">>, <<"service-unavailable">>),
  272:             message_is_not_delivered(User1c, [User2], <<"grrr!">>),
  273:             privacy_helper:gets_error(User1c, <<"cancel">>, <<"service-unavailable">>),
  274:             ct:sleep(100),
  275:             escalus_assert:has_no_stanzas(User2)
  276:         end).
  277: 
  278: blocking_doesnt_interfere(Config) ->
  279:     escalus:fresh_story(
  280:         Config, [{alice, 1}, {bob, 1}, {kate, 1}],
  281:         fun(User1, User2, User3) ->
  282:             %% given
  283:             user_blocks(User1, [User2]),
  284:             %% then
  285:             message_is_not_delivered(User2, [User1], <<"!@#@$@#$%">>),
  286:             privacy_helper:gets_error(User2, <<"cancel">>, <<"service-unavailable">>),
  287:             message_is_delivered(User3, [User1], <<"Ni hao.">>)
  288:         end).
  289: 
  290: blocking_propagates_to_resources(Config) ->
  291:     escalus:fresh_story(
  292:         Config, [{alice, 2}, {bob, 1}],
  293:         fun(User1a, User1b, User2) ->
  294:             %% given
  295:             user_blocks(User1a, [User2]),
  296:             %% then
  297:             client_gets_block_iq(User1b),
  298:             % Alice can't send from any of her resources
  299:             message_is_not_delivered(User1a, [User2], <<"roar!">>),
  300:             client_gets_blocking_error(User1a),
  301:             message_is_not_delivered(User1b, [User2], <<"woof!">>),
  302:             client_gets_blocking_error(User1b),
  303:             % Bob can't send to any of Alice's resources
  304:             message_is_not_delivered(User2, [User1a], <<"hau!">>),
  305:             privacy_helper:gets_error(User2, <<"cancel">>, <<"service-unavailable">>),
  306:             message_is_not_delivered(User2, [User1b], <<"miau!">>),
  307:             privacy_helper:gets_error(User2, <<"cancel">>, <<"service-unavailable">>)
  308:         end).
  309: 
  310: iq_reply_doesnt_crash_user_process(Config) ->
  311:     escalus:fresh_story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) ->
  312: 
  313:         QueryWithBlockingNS = escalus_stanza:query_el(?NS_BLOCKING, []),
  314:         %% Send IQ reply with blocking ns
  315:         %% Send message to check user process still alive
  316:         privacy_helper:does_user_process_crash(Alice,
  317:             Bob,
  318:             <<"error">>,
  319:             QueryWithBlockingNS,
  320:             <<"Hello, Bob">>),
  321: 
  322:         privacy_helper:does_user_process_crash(Bob,
  323:             Alice,
  324:             <<"result">>,
  325:             QueryWithBlockingNS,
  326:             <<"Hello, Alice">>)
  327:     end).
  328: 
  329: messages_after_relogin(Config) ->
  330:     %% given
  331:     escalus:story(
  332:         Config, [{alice, 1}, {bob, 1}],
  333:         fun(User1, User2) ->
  334:             user_blocks(User1, [User2])
  335:         end),
  336:     %% XXX Because alice can receive presence unavalable from alice
  337:     %% XXX It's a potential bug, please test it.
  338:     %% XXX has_stanzas_but_shouldnt
  339:     %% XXX reported as https://github.com/esl/MongooseIM/issues/1799
  340:     mongoose_helper:kick_everyone(),
  341:     escalus:story(
  342:         Config, [{alice, 1}, {bob, 1}],
  343:         fun(User1, User2) ->
  344:             message_is_not_delivered(User2, [User1], <<"Hey alice, are you there?">>),
  345:             message_is_delivered(User1, [User1], <<"Hey bob, carpe diem!">>)
  346:         end).
  347: 
  348: messages_arrive_after_unblock_and_relogin(Config) ->
  349:     %% given when
  350:     escalus:story(
  351:         Config, [{alice, 1}, {bob, 1}],
  352:         fun(User1, User2) ->
  353:             user_unblocks(User1, [User2])
  354:         end),
  355:     %% then
  356:     escalus:story(
  357:         Config, [{alice, 1}, {bob, 1}],
  358:         fun(User1, User2) ->
  359:             message_is_delivered(User2, [User1], <<"Hey bob, are you there?">>)
  360:         end).
  361: 
  362: blocking_and_relogin_many(Config) ->
  363:     %% given
  364:     simple_story(Config,
  365:         fun(User1, User2, User3, User4, _) ->
  366:             user_blocks(User1, [User2, User3]),
  367:             user_blocks(User1, [User3, User4])
  368:         end),
  369:     %% when
  370:     simple_story(Config,
  371:         fun(User1, User2, _, User4, User5) ->
  372:             user_unblocks(User1,  [User4]),
  373:             user_unblocks(User1,  [User4, User5]),
  374:             user_unblocks(User1,  [User2, User5])
  375:         end),
  376:     %% then
  377:     simple_story(Config,
  378:         fun(User1, User2, User3, User4, _) ->
  379:             message_is_delivered(User1, [User4], <<"Under the bridge!">>),
  380:             message_is_not_delivered(User1, [User3], <<"Cant stop">>),
  381:             client_gets_blocking_error(User1),
  382:             message_is_delivered(User1, [User2], <<"House of th rising sun">>),
  383:             BlockList = get_blocklist(User1),
  384:             blocklist_contains_jid(BlockList, User3)
  385:         end).
  386: 
  387: simple_story(Config, Fun) ->
  388:     escalus:story(
  389:         Config, [{alice, 1}, {bob, 1}, {kate, 1}, {mike, 1}, {john, 1}],
  390:         Fun
  391:     ).
  392: 
  393: clear_list_relogin(Config) ->
  394:     %% unexpected presence unavailable
  395:     mongoose_helper:kick_everyone(),
  396:     escalus:story(
  397:         Config, [{alice, 1}, {bob, 1}],
  398:         fun(User1, User2) ->
  399:             user_blocks(User1, [User2])
  400:         end),
  401:     escalus:story(
  402:         Config, [{alice, 1}],
  403:         fun(User1) ->
  404:             user_unblocks_all(User1)
  405:         end),
  406:     escalus:story(
  407:         Config, [{alice, 1}, {bob, 1}],
  408:         fun(User1, User2) ->
  409:             message_is_delivered(User1, [User2], <<"Doom and gloom!">>)
  410:         end).
  411: 
  412: blocker_cant_send_to_blockee(Config) ->
  413:     escalus:fresh_story(
  414:         Config, [{alice, 1}, {bob, 1}],
  415:         fun(User1, User2) ->
  416:             user_blocks(User1, [User2]),
  417:             message(User1, User2, <<"I'm not talking to you!">>),
  418:             client_gets_blocking_error(User1)
  419:         end).
  420: 
  421: block_push_sent(Config) ->
  422:     %% make sure privacy list push arrives to all the user's resources
  423:     escalus:fresh_story(
  424:         Config, [{alice, 2}, {bob, 2}],
  425:         fun(User1a, User1b, User2a, _User2b) ->
  426:             user_blocks(User1a, [User2a]),
  427:             client_gets_block_iq(User1b)
  428:         end).
  429: 
  430: notify_blockee(Config) ->
  431:     %% as XEP-0191 says, when we block a user he should receive 'unavailable', and a contrario.
  432:     escalus:fresh_story(
  433:         Config, [{alice, 1}, {bob, 1}],
  434:         fun(Alice, Bob) ->
  435:             %% make sure they're friends and Bob receives Alice's presences
  436:             subscribe(Bob, Alice),
  437:             escalus:send(Alice, escalus_stanza:presence(<<"available">>)),
  438:             escalus:assert(is_presence_with_type, [<<"available">>],
  439:                            escalus:wait_for_stanza(Alice)),
  440:             escalus:assert(is_presence_with_type, [<<"available">>],
  441:                            escalus:wait_for_stanza(Bob)),
  442:             user_blocks(Alice, [Bob]),
  443:             escalus:assert(is_presence_with_type, [<<"unavailable">>],
  444:                            escalus:wait_for_stanza(Bob)),
  445:             user_unblocks(Alice, [Bob]),
  446:             escalus:assert(is_presence_with_type, [<<"available">>],
  447:                            escalus:wait_for_stanza(Bob))
  448:         end).
  449: 
  450: %% common
  451: 
  452: %%
  453: get_blocklist(User) ->
  454:     IQGet = get_blocklist_stanza(),
  455:     escalus_client:send(User, IQGet),
  456:     escalus_client:wait_for_stanza(User).
  457: 
  458: %%
  459: %% stanza generators
  460: %%
  461: 
  462: get_blocklist_stanza() ->
  463:     Payload = #xmlel{name = <<"blocklist">>,
  464:         attrs=[{<<"xmlns">>, ?NS_BLOCKING}]},
  465:     #xmlel{name = <<"iq">>,
  466:         attrs = [{<<"type">>, <<"get">>}],
  467:         children = [Payload]}.
  468: 
  469: block_users_stanza(UsersToBlock) ->
  470:     Childs = [item_el(U) || U <- UsersToBlock],
  471:     Payload = #xmlel{name = <<"block">>,
  472:         attrs=[{<<"xmlns">>, ?NS_BLOCKING}],
  473:         children = Childs
  474:     },
  475:     #xmlel{name = <<"iq">>,
  476:         attrs = [{<<"type">>, <<"set">>}],
  477:         children = [Payload]}.
  478: 
  479: block_users_stanza_with_white_spaces(UsersToBlock) ->
  480:     Childs = [item_el(U) || U <- UsersToBlock],
  481:     % when client adds some white characters in blocking list
  482:     WhiteSpacedChilds = Childs ++ [{xmlcdata, "\n"}],
  483:     Payload = #xmlel{name = <<"block">>,
  484:         attrs=[{<<"xmlns">>, ?NS_BLOCKING}],
  485:         children = WhiteSpacedChilds
  486:     },
  487:     #xmlel{name = <<"iq">>,
  488:         attrs = [{<<"type">>, <<"set">>}],
  489:         children = [Payload]}.
  490: 
  491: 
  492: %%block_user_stanza(UserToBlock) ->
  493: %%    Payload = #xmlel{name = <<"block">>,
  494: %%        attrs=[{<<"xmlns">>, ?NS_BLOCKING}],
  495: %%        children = [item_el(UserToBlock)]
  496: %%    },
  497: %%    #xmlel{name = <<"iq">>,
  498: %%        attrs = [{<<"type">>, <<"set">>}],
  499: %%        children = Payload}.
  500: 
  501: unblock_user_stanza(UserToUnblock) ->
  502:     Payload = #xmlel{name = <<"unblock">>,
  503:         attrs=[{<<"xmlns">>, ?NS_BLOCKING}],
  504:         children = [item_el(UserToUnblock)]
  505:     },
  506:     #xmlel{name = <<"iq">>,
  507:         attrs = [{<<"type">>, <<"set">>}],
  508:         children = [Payload]}.
  509: 
  510: unblock_users_stanza(UsersToBlock) ->
  511:     Childs = [item_el(U) || U <- UsersToBlock],
  512:     Payload = #xmlel{name = <<"unblock">>,
  513:         attrs=[{<<"xmlns">>, ?NS_BLOCKING}],
  514:         children = Childs
  515:     },
  516:     #xmlel{name = <<"iq">>,
  517:         attrs = [{<<"type">>, <<"set">>}],
  518:         children = [Payload]}.
  519: 
  520: unblock_all_stanza() ->
  521:     Payload = #xmlel{name = <<"unblock">>,
  522:         attrs=[{<<"xmlns">>, ?NS_BLOCKING}],
  523:         children = []
  524:     },
  525:     #xmlel{name = <<"iq">>,
  526:         attrs = [{<<"type">>, <<"set">>}],
  527:         children = [Payload]}.
  528: 
  529: item_el(User) when is_binary(User) ->
  530:     #xmlel{name = <<"item">>,
  531:         attrs = [{<<"jid">>, User}]}.
  532: %%
  533: %% predicates
  534: %%
  535: 
  536: is_xep191_not_available(#xmlel{} = Stanza) ->
  537:     ErrorEl = exml_query:subelement(Stanza, <<"error">>),
  538:     <<"error">> == exml_query:attr(Stanza, <<"type">>)
  539:         andalso
  540:         undefined =/= exml_query:subelement(ErrorEl, <<"not-acceptable">>)
  541:         andalso
  542:         undefined =/= exml_query:subelement(ErrorEl, <<"blocked">>)
  543:         andalso
  544:         <<"urn:xmpp:blocking:errors">> ==
  545:             exml_query:path(ErrorEl, [{element, <<"blocked">>},
  546:                 {attr, <<"xmlns">>}]).
  547: 
  548: 
  549: is_blocklist_result_empty(#xmlel{children = [#xmlel{name =Name,
  550:     attrs = Attrs,
  551:     children= Child}]} = Stanza) ->
  552:     true = escalus_pred:is_iq(Stanza),
  553:     <<"blocklist">> = Name,
  554:     {<<"xmlns">>, ?NS_BLOCKING} = lists:keyfind(<<"xmlns">>, 1, Attrs),
  555:     [] = Child,
  556:     true.
  557: 
  558: blocklist_result_has(ExpectedUser, Stanza) ->
  559:     true = escalus_pred:is_iq(Stanza),
  560:     Blocklist = hd(Stanza#xmlel.children),
  561:     Attrs = Blocklist#xmlel.attrs,
  562:     Children = Blocklist#xmlel.children,
  563:     <<"blocklist">> = Blocklist#xmlel.name,
  564:     {<<"xmlns">>, ?NS_BLOCKING} = lists:keyfind(<<"xmlns">>, 1, Attrs),
  565:     true == lists:member(ExpectedUser, get_blocklist_items(Children)).
  566: 
  567: is_xep191_push(Type, #xmlel{attrs = A, children = [#xmlel{name = Type,
  568:     attrs = Attrs}]}=Stanza) ->
  569:     true = escalus_pred:is_iq_set(Stanza),
  570:     {<<"id">>, <<"push">>} = lists:keyfind(<<"id">>, 1, A),
  571:     {<<"xmlns">>, ?NS_BLOCKING} = lists:keyfind(<<"xmlns">>, 1, Attrs),
  572:     true.
  573: 
  574: is_xep191_push(Type, [], #xmlel{children = [#xmlel{name = Type, children = []}]}=Stanza) ->
  575:     is_xep191_push(Type, Stanza);
  576: is_xep191_push(Type, [], #xmlel{children = [#xmlel{name = Type, children = _}]}) ->
  577:     false;
  578: is_xep191_push(Type, JIDs, #xmlel{attrs = _, children = [#xmlel{name = Type,
  579:     attrs = Attrs, children = Items}]}=Stanza) ->
  580:     true = escalus_pred:is_iq_set(Stanza),
  581:     {<<"xmlns">>, ?NS_BLOCKING} = lists:keyfind(<<"xmlns">>, 1, Attrs),
  582:     F = fun(El) ->
  583:         #xmlel{name = <<"item">>, attrs =  [{<<"jid">>, Value}]} = El,
  584:         lists:member(Value, JIDs)
  585:         end,
  586:     TrueList = lists:map(F, Items),
  587:     lists:all(fun(El) -> El end, TrueList);
  588: is_xep191_push(_, _, _) ->
  589:     false.
  590: 
  591: %%
  592: %% helpers
  593: %%
  594: 
  595: bare(C) ->  escalus_utils:jid_to_lower(escalus_client:short_jid(C)).
  596: 
  597: get_blocklist_items(Items) ->
  598:     lists:map(fun(#xmlel{name = <<"item">>, attrs=A}) ->
  599:         {_, R} = lists:keyfind(<<"jid">>, 1, A),
  600:         R
  601:               end, Items).
  602: 
  603: user_blocks(Blocker, Blockees) when is_list(Blockees) ->
  604:     BlockeeJIDs = [ escalus_utils:jid_to_lower(escalus_client:short_jid(B)) || B <- Blockees ],
  605:     AddStanza = block_users_stanza(BlockeeJIDs),
  606:     escalus_client:send(Blocker, AddStanza),
  607:     Res = escalus:wait_for_stanzas(Blocker, 2),
  608:     CheckPush = fun(E) -> is_xep191_push(<<"block">>, BlockeeJIDs, E) end,
  609:     Preds = [is_iq_result, CheckPush],
  610:     escalus:assert_many(Preds, Res).
  611: 
  612: blocklist_is_empty(BlockList) ->
  613:     escalus:assert(is_iq_result, BlockList),
  614:     escalus:assert(fun is_blocklist_result_empty/1, BlockList).
  615: 
  616: blocklist_contains_jid(BlockList, Client) ->
  617:     JID = escalus_utils:jid_to_lower(escalus_client:short_jid(Client)),
  618:     escalus:assert(fun blocklist_result_has/2, [JID], BlockList).
  619: 
  620: user_unblocks(Unblocker, Unblockees) when is_list(Unblockees) ->
  621:     UnblockeeJIDs = [ escalus_utils:jid_to_lower(escalus_client:short_jid(B)) || B <- Unblockees ],
  622:     AddStanza = unblock_users_stanza(UnblockeeJIDs),
  623:     escalus_client:send(Unblocker, AddStanza),
  624:     Res = escalus:wait_for_stanzas(Unblocker, 2),
  625:     CheckPush = fun(E) -> is_xep191_push(<<"unblock">>, UnblockeeJIDs, E) end,
  626:     Preds = [is_iq_result, CheckPush],
  627:     escalus:assert_many(Preds, Res);
  628: user_unblocks(Unblocker, Unblockee) ->
  629:     JID = escalus_utils:jid_to_lower(escalus_client:short_jid(Unblockee)),
  630:     escalus_client:send(Unblocker, unblock_user_stanza(JID)),
  631:     user_gets_remove_result(Unblocker, [JID]).
  632: 
  633: blocklist_doesnt_contain_jid(BlockList, Client) ->
  634:     JID = escalus_utils:jid_to_lower(escalus_client:short_jid(Client)),
  635:     escalus:assert(is_iq_result, BlockList),
  636:     ?assertNot(blocklist_result_has(JID, BlockList)).
  637: 
  638: user_gets_remove_result(Client, ContactList) ->
  639:     RemoveResult = escalus:wait_for_stanzas(Client, 2),
  640:     CheckPush = fun(E) -> is_xep191_push(<<"unblock">>, ContactList, E) end,
  641:     Preds = [is_iq_result, CheckPush],
  642:     escalus:assert_many(Preds, RemoveResult).
  643: 
  644: 
  645: user_unblocks_all(User) ->
  646:     escalus_client:send(User, unblock_all_stanza()),
  647:     user_gets_remove_result(User, []).
  648: 
  649: message(From, To, MsgTxt) ->
  650:     escalus_client:send(From, escalus_stanza:chat_to(To, MsgTxt)).
  651: 
  652: message_is_delivered(From, [To|_] = Tos, MessageText) ->
  653:     BareTo = escalus_utils:jid_to_lower(escalus_client:short_jid(To)),
  654:     escalus:send(From, escalus_stanza:chat_to(BareTo, MessageText)),
  655:     [ escalus:assert(is_chat_message, [MessageText], escalus:wait_for_stanza(C)) ||
  656:         C <- Tos ];
  657: message_is_delivered(From, To, MessageText) ->
  658:     BareTo =  escalus_utils:jid_to_lower(escalus_client:short_jid(To)),
  659:     escalus:send(From, escalus_stanza:chat_to(BareTo, MessageText)),
  660:     escalus:assert(is_chat_message, [MessageText], escalus:wait_for_stanza(To)).
  661: 
  662: message_is_not_delivered(From, [To|_] = Tos, MessageText) ->
  663:     BareTo = escalus_utils:jid_to_lower(escalus_client:short_jid(To)),
  664:     escalus:send(From, escalus_stanza:chat_to(BareTo, MessageText)),
  665:     clients_have_no_messages(Tos).
  666: 
  667: clients_have_no_messages(Cs) when is_list (Cs) -> [ client_has_no_messages(C) || C <- Cs ].
  668: 
  669: client_has_no_messages(C) -> escalus_assert:has_no_stanzas(C).
  670: 
  671: client_gets_blocking_error(C) ->
  672:     Stanza = escalus_client:wait_for_stanza(C),
  673:     escalus:assert(fun is_xep191_not_available/1, [], Stanza).
  674: 
  675: client_gets_block_iq(C) ->
  676:     escalus:assert(fun is_xep191_push/2, [<<"block">>], escalus:wait_for_stanza(C)).
  677: 
  678: flush(User) ->
  679:     escalus:wait_for_stanzas(User, 10, 100).
  680: 
  681: add_sample_contact(Alice, Bob) ->
  682:     escalus:send(Alice, escalus_stanza:roster_add_contact(Bob,
  683:         [<<"friends">>],
  684:         <<"Bobby">>)),
  685: 
  686:     Received = escalus:wait_for_stanzas(Alice, 2),
  687:     escalus:assert_many([is_roster_set, is_iq_result], Received),
  688: 
  689:     Result = hd([R || R <- Received, escalus_pred:is_roster_set(R)]),
  690:     escalus:assert(count_roster_items, [1], Result),
  691:     escalus:send(Alice, escalus_stanza:iq_result(Result)).
  692: 
  693: 
  694: subscribe(Bob, Alice) ->
  695:     %% Bob adds Alice as a contact
  696:     add_sample_contact(Bob, Alice),
  697:     %% He subscribes to her presences
  698:     escalus:send(Bob, escalus_stanza:presence_direct(alice, <<"subscribe">>)),
  699:     PushReq = escalus:wait_for_stanza(Bob),
  700:     escalus:assert(is_roster_set, PushReq),
  701:     escalus:send(Bob, escalus_stanza:iq_result(PushReq)),
  702:     %% Alice receives subscription request
  703:     Received = escalus:wait_for_stanza(Alice),
  704:     escalus:assert(is_presence_with_type, [<<"subscribe">>], Received),
  705:     %% Alice adds new contact to his roster
  706:     escalus:send(Alice, escalus_stanza:roster_add_contact(Bob,
  707:         [<<"enemies">>],
  708:         <<"Bob">>)),
  709:     PushReqB = escalus:wait_for_stanza(Alice),
  710:     escalus:assert(is_roster_set, PushReqB),
  711:     escalus:send(Alice, escalus_stanza:iq_result(PushReqB)),
  712:     escalus:assert(is_iq_result, escalus:wait_for_stanza(Alice)),
  713:     %% Alice sends subscribed presence
  714:     escalus:send(Alice, escalus_stanza:presence_direct(bob, <<"subscribed">>)),
  715:     %% Bob receives subscribed
  716:     Stanzas = escalus:wait_for_stanzas(Bob, 2),
  717:     check_subscription_stanzas(Stanzas, <<"subscribed">>),
  718:     escalus:assert(is_presence, escalus:wait_for_stanza(Bob)),
  719:     %% Alice receives roster push
  720:     PushReqB1 = escalus:wait_for_stanza(Alice),
  721:     escalus:assert(is_roster_set, PushReqB1),
  722:     %% Alice sends presence
  723:     escalus:send(Alice, escalus_stanza:presence(<<"available">>)),
  724:     escalus:assert(is_presence, escalus:wait_for_stanza(Bob)),
  725:     escalus:assert(is_presence, escalus:wait_for_stanza(Alice)),
  726:     ok.
  727: 
  728: 
  729: check_subscription_stanzas(Stanzas, Type) ->
  730:     IsPresWithType = fun(S) -> escalus_pred:is_presence_with_type(Type, S) end,
  731:     escalus:assert_many([is_roster_set, IsPresWithType], Stanzas).