1: %%%===================================================================
    2: %%% @copyright (C) 2015, Erlang Solutions Ltd.
    3: %%% @doc Suite for testing pubsub features as described in XEP-0060
    4: %%% @end
    5: %%%===================================================================
    6: 
    7: -module(pubsub_SUITE).
    8: 
    9: -compile([export_all, nowarn_export_all]).
   10: 
   11: -include_lib("escalus/include/escalus_xmlns.hrl").
   12: -include_lib("exml/include/exml.hrl").
   13: -include_lib("eunit/include/eunit.hrl").
   14: 
   15: -import(pubsub_tools, [pubsub_node/0,
   16:                        domain/0,
   17:                        node_addr/0,
   18:                        encode_group_name/2,
   19:                        decode_group_name/1,
   20:                        nodetree_to_mod/1]).
   21: -import(distributed_helper, [mim/0,
   22:                              require_rpc_nodes/1,
   23:                              subhost_pattern/1,
   24:                              rpc/4]).
   25: 
   26: %%--------------------------------------------------------------------
   27: %% Suite configuration
   28: %%--------------------------------------------------------------------
   29: 
   30: suite() ->
   31:     require_rpc_nodes([mim]) ++ escalus:suite().
   32: 
   33: all() ->
   34:     [{group, GN} || {GN, _, _} <- groups()].
   35: 
   36: groups() ->
   37:     lists:flatmap(
   38:       fun(NodeTree) ->
   39:               [ {encode_group_name(BaseGroup, NodeTree), Opts, Cases}
   40:                 || {BaseGroup, Opts, Cases} <- base_groups(),
   41:                    group_is_compatible(BaseGroup, NodeTree) ]
   42:       end, [<<"dag">>, <<"tree">>]).
   43: 
   44: % nodetree_tree doesn't support collections by XEP
   45: % It uses implicit collections by path in nodes' names
   46: group_is_compatible(collection, <<"tree">>) -> false;
   47: group_is_compatible(collection_config, <<"tree">>) -> false;
   48: group_is_compatible(hometree_specific, OnlyNodetreeTree) -> OnlyNodetreeTree =:= <<"tree">>;
   49: group_is_compatible(_, _) -> true.
   50: 
   51: base_groups() ->
   52:     [{basic, [parallel], basic_tests()},
   53:      {service_config, [parallel], service_config_tests()},
   54:      {node_config, [parallel], node_config_tests()},
   55:      {node_affiliations, [parallel], node_affiliations_tests()},
   56:      {manage_subscriptions, [parallel], manage_subscriptions_tests()},
   57:      {collection, [sequence], collection_tests()},
   58:      {collection_config, [parallel], collection_config_tests()},
   59:      {debug_calls, [parallel], debug_calls_tests()},
   60:      {pubsub_item_publisher_option, [parallel], pubsub_item_publisher_option_tests()},
   61:      {hometree_specific, [sequence], hometree_specific_tests()},
   62:      {last_item_cache, [parallel], last_item_cache_tests()}].
   63: 
   64: basic_tests() ->
   65:     [
   66:      discover_features_test,
   67:      discover_service_features_test,
   68:      discover_sm_features_test,
   69:      discover_nodes_test,
   70:      create_delete_node_test,
   71:      create_node_errors_test,
   72:      subscribe_unsubscribe_test,
   73:      subscribe_options_test,
   74:      subscribe_options_deliver_option_test,
   75:      subscribe_options_separate_request_test,
   76:      publish_test,
   77:      publish_with_max_items_test,
   78:      publish_with_existing_id_test,
   79:      notify_test,
   80:      request_all_items_test,
   81:      request_particular_item_test,
   82:      retract_test,
   83:      retract_when_user_goes_offline_test,
   84:      purge_all_items_test,
   85:      publish_only_retract_items_scope_test
   86:     ].
   87: 
   88: service_config_tests() ->
   89:     [
   90:      max_subscriptions_test
   91:     ].
   92: 
   93: node_config_tests() ->
   94:     [
   95:      retrieve_default_configuration_test,
   96:      retrieve_configuration_test,
   97:      set_configuration_test,
   98:      set_configuration_errors_test,
   99:      notify_config_test,
  100:      disable_notifications_test,
  101:      disable_payload_test,
  102:      disable_persist_items_test,
  103:      notify_only_available_users_test,
  104:      notify_unavailable_user_test,
  105:      send_last_published_item_test
  106:     ].
  107: 
  108: node_affiliations_tests() ->
  109:     [
  110:      get_affiliations_test,
  111:      add_publisher_and_member_test,
  112:      swap_owners_test,
  113:      deny_no_owner_test
  114:     ].
  115: 
  116: manage_subscriptions_tests() ->
  117:     [
  118:      retrieve_user_subscriptions_test,
  119:      retrieve_node_subscriptions_test,
  120:      modify_node_subscriptions_test,
  121:      process_subscription_requests_test,
  122:      retrieve_pending_subscription_requests_test,
  123:      retrieve_pending_subscription_requests_errors_test
  124:     ].
  125: 
  126: collection_tests() ->
  127:     [
  128:      create_delete_collection_test,
  129:      subscribe_unsubscribe_collection_test,
  130:      collection_delete_makes_leaf_parentless,
  131:      notify_collection_test,
  132:      notify_collection_leaf_and_item_test,
  133:      notify_collection_bare_jid_test,
  134:      notify_collection_and_leaf_test,
  135:      notify_collection_and_leaf_same_user_test,
  136:      notify_collections_with_same_leaf_test,
  137:      notify_nested_collections_test,
  138:      retrieve_subscriptions_collection_test,
  139:      discover_top_level_nodes_test,
  140:      discover_child_nodes_test,
  141:      request_all_items_leaf_test
  142:     ].
  143: 
  144: collection_config_tests() ->
  145:     [
  146:      disable_notifications_leaf_test,
  147:      disable_payload_leaf_test,
  148:      disable_persist_items_leaf_test
  149:     ].
  150: 
  151: debug_calls_tests() ->
  152:     [
  153:      debug_get_items_test,
  154:      debug_get_item_test
  155:     ].
  156: 
  157: pubsub_item_publisher_option_tests() ->
  158:     [
  159:      get_item_with_publisher_option_test,
  160:      receive_item_notification_with_publisher_option_test
  161:     ].
  162: 
  163: hometree_specific_tests() ->
  164:     [
  165:      can_create_node_with_existing_parent_path,
  166:      cant_create_node_with_missing_parent_path,
  167:      disco_node_children_by_path_prefix,
  168:      deleting_parent_path_deletes_children
  169:     ].
  170: 
  171: last_item_cache_tests() ->
  172:     [
  173:      send_last_published_item_test,
  174:      send_last_published_item_no_items_test,
  175:      purge_all_items_test
  176:     ].
  177: %%--------------------------------------------------------------------
  178: %% Init & teardown
  179: %%--------------------------------------------------------------------
  180: 
  181: init_per_suite(Config) ->
  182:     escalus:init_per_suite(Config).
  183: 
  184: end_per_suite(Config) ->
  185:     escalus_fresh:clean(),
  186:     escalus:end_per_suite(Config).
  187: 
  188: init_per_group(ComplexName, Config) ->
  189:     DecodedGroupName = decode_group_name(ComplexName),
  190:     ExtraOptions = extra_options_by_group_name(DecodedGroupName),
  191:     Config2 = dynamic_modules:save_modules(domain(), Config),
  192:     dynamic_modules:ensure_modules(domain(), required_modules(ExtraOptions)),
  193:     Config2.
  194: 
  195: extra_options_by_group_name(#{ node_tree := NodeTree,
  196:                                base_name := pubsub_item_publisher_option }) ->
  197:     #{nodetree => nodetree_to_mod(NodeTree),
  198:       plugins => [plugin_by_nodetree(NodeTree)],
  199:       item_publisher => true};
  200: extra_options_by_group_name(#{ node_tree := NodeTree,
  201:                                base_name := hometree_specific }) ->
  202:     #{nodetree => nodetree_to_mod(NodeTree),
  203:       plugins => [<<"hometree">>]};
  204: extra_options_by_group_name(#{ node_tree := NodeTree,
  205:                                base_name := last_item_cache}) ->
  206:     #{nodetree => nodetree_to_mod(NodeTree),
  207:       plugins => [plugin_by_nodetree(NodeTree)],
  208:       last_item_cache => mongoose_helper:mnesia_or_rdbms_backend()};
  209: extra_options_by_group_name(#{base_name := service_config}) ->
  210:     #{max_subscriptions_node => 1};
  211: extra_options_by_group_name(#{ node_tree := NodeTree }) ->
  212:     #{nodetree => nodetree_to_mod(NodeTree),
  213:       plugins => [plugin_by_nodetree(NodeTree)]}.
  214: 
  215: plugin_by_nodetree(<<"dag">>) -> <<"dag">>;
  216: plugin_by_nodetree(<<"tree">>) -> <<"flat">>.
  217: 
  218: end_per_group(_GroupName, Config) ->
  219:     dynamic_modules:restore_modules(Config).
  220: 
  221: init_per_testcase(notify_unavailable_user_test, _Config) ->
  222:     {skip, "mod_offline does not store events"};
  223: init_per_testcase(_TestName, Config) ->
  224:     escalus:init_per_testcase(_TestName, Config).
  225: 
  226: end_per_testcase(TestName, Config) ->
  227:     escalus:end_per_testcase(TestName, Config).
  228: 
  229: %%--------------------------------------------------------------------
  230: %% Test cases for XEP-0060
  231: %% Comments in test cases refer to sections is the XEP
  232: %%--------------------------------------------------------------------
  233: 
  234: %%--------------------------------------------------------------------
  235: %% Main PubSub cases
  236: %%--------------------------------------------------------------------
  237: 
  238: discover_features_test(Config) ->
  239:     escalus:fresh_story(
  240:       Config,
  241:       [{alice, 1}],
  242:       fun(Alice) ->
  243:               Server = escalus_client:server(Alice),
  244:               escalus:send(Alice, escalus_stanza:disco_info(Server)),
  245:               Stanza = escalus:wait_for_stanza(Alice),
  246:               escalus:assert(has_feature, [?NS_PUBSUB], Stanza)
  247:       end).
  248: 
  249: discover_service_features_test(Config) ->
  250:     escalus:fresh_story(
  251:       Config,
  252:       [{alice, 1}],
  253:       fun(Alice) ->
  254:               escalus:send(Alice, escalus_stanza:disco_info(node_addr())),
  255:               Stanza = escalus:wait_for_stanza(Alice),
  256:               escalus:assert(has_identity, [<<"pubsub">>, <<"service">>], Stanza),
  257:               escalus:assert(has_feature, [?NS_PUBSUB], Stanza)
  258:       end).
  259: 
  260: discover_sm_features_test(Config) ->
  261:     escalus:fresh_story(Config, [{alice, 1}],
  262:         fun(Alice) ->
  263:                 AliceJid = escalus_client:short_jid(Alice),
  264:                 escalus:send(Alice, escalus_stanza:disco_info(AliceJid)),
  265:                 Stanza = escalus:wait_for_stanza(Alice),
  266:                 %% The feature shouldn't be present when PEP is disabled
  267:                 ?assertNot(escalus_pred:has_feature(?NS_PUBSUB, Stanza)),
  268:                 escalus:assert(is_stanza_from, [AliceJid], Stanza)
  269:         end).
  270: 
  271: discover_nodes_test(Config) ->
  272:     escalus:fresh_story(
  273:       Config,
  274:       [{alice, 1}, {bob, 1}],
  275:       fun(Alice, Bob) ->
  276:               %% Request:  5.2 Ex.9  Entity asks service for all first-level nodes
  277:               %% Response:     Ex.10 Service returns all first-level nodes
  278:               %% it shouldn't contain the Node which will be created in a moment
  279:               {_, NodeName} = Node = pubsub_node(),
  280:               pubsub_tools:discover_nodes(Bob, node_addr(), [{expected_result, [{no, NodeName}]}]),
  281: 
  282:               pubsub_tools:create_node(Alice, Node, []),
  283:               pubsub_tools:discover_nodes(Bob, node_addr(), [{expected_result, [NodeName]}]),
  284: 
  285:               {_, NodeName2} = Node2 = pubsub_node(),
  286:               pubsub_tools:create_node(Alice, Node2, []),
  287:               pubsub_tools:discover_nodes(
  288:                 Bob, node_addr(), [{expected_result, [NodeName, NodeName2]}]),
  289: 
  290:               pubsub_tools:delete_node(Alice, Node, []),
  291:               pubsub_tools:delete_node(Alice, Node2, [])
  292:       end).
  293: 
  294: create_delete_node_test(Config) ->
  295:     escalus:fresh_story(
  296:       Config,
  297:       [{alice, 1}],
  298:       fun(Alice) ->
  299:               %% Request:  8.1.2 Ex.132 create node with (default) open access model
  300:               %% Response:       Ex.134 success
  301:               %%                        Note: contains node ID although XEP does not require this
  302:               Node = pubsub_node(),
  303:               pubsub_tools:create_node(Alice, Node, []),
  304: 
  305:               %% Request:  8.4.1 Ex.155 owner deletes a node
  306:               %% Response:       Ex.157 success
  307:               pubsub_tools:delete_node(Alice, Node, [])
  308:       end).
  309: 
  310: create_node_errors_test(Config) ->
  311:     escalus:fresh_story(
  312:       Config,
  313:       [{alice, 1}],
  314:       fun(Alice) ->
  315:               Node = pubsub_node(),
  316:               GoodOpts = [{<<"pubsub#notify_config">>, <<"1">>}],
  317:               BadOpts = [{<<"pubsub#notify_config">>, <<"7">>}],
  318: 
  319:               %% Invalid forms
  320:               pubsub_tools:create_node(Alice, Node,
  321:                                        [{modify_request, fun form_helper:remove_form_types/1},
  322:                                         {expected_error_type, <<"modify">>},
  323:                                         {config, GoodOpts}]),
  324:               pubsub_tools:create_node(Alice, Node,
  325:                                        [{expected_error_type, <<"modify">>},
  326:                                         {config, BadOpts}]),
  327: 
  328:               %% Empty configuration element should be accepted
  329:               pubsub_tools:create_node(Alice, Node,
  330:                                        [{modify_request, fun form_helper:remove_forms/1},
  331:                                         {config, GoodOpts}]),
  332:               pubsub_tools:delete_node(Alice, Node, [])
  333:       end).
  334: 
  335: subscribe_unsubscribe_test(Config) ->
  336:     escalus:fresh_story(
  337:       Config,
  338:       [{alice, 1}, {bob, 1}],
  339:       fun(Alice, Bob) ->
  340:               Node = pubsub_node(),
  341:               pubsub_tools:create_node(Alice, Node, []),
  342: 
  343:               %% Request:  6.1.1 Ex.32 entity subscribes to a node
  344:               %% Response: 6.1.2 Ex.33 success (with subscription ID)
  345:               pubsub_tools:subscribe(Bob, Node, []),
  346: 
  347:               %% Request:  6.2.1 Ex.51 unsubscribe from a node
  348:               %% Response: 6.2.2 Ex.52 success
  349:               pubsub_tools:unsubscribe(Bob, Node, []),
  350: 
  351:               %% Check subscriptions without resources
  352:               pubsub_tools:subscribe(Bob, Node, [{jid_type, bare}]),
  353:               pubsub_tools:unsubscribe(Bob, Node, [{jid_type, bare}]),
  354: 
  355:               pubsub_tools:delete_node(Alice, Node, [])
  356:       end).
  357: 
  358: subscribe_options_test(Config) ->
  359:     escalus:fresh_story(
  360:       Config,
  361:       [{alice, 1}, {bob, 1}, {geralt, 1}],
  362:       fun(Alice, Bob, Geralt) ->
  363:               {_, NodeName} = Node = pubsub_node(),
  364:               pubsub_tools:create_node(Alice, Node, []),
  365: 
  366:               %% 6.3.4.2 Example 62. No such subscriber
  367:               [ pubsub_tools:get_subscription_options(Client, {node_addr(), NodeName},
  368:                                                       [{expected_error_type, <<"modify">>}])
  369:                 || Client <- [Alice, Bob, Geralt] ],
  370: 
  371:               GeraltOpts = [{<<"pubsub#deliver">>, <<"true">>}],
  372:               BobOpts = [{<<"pubsub#deliver">>, <<"false">>}],
  373:               pubsub_tools:subscribe(Geralt, Node, [{config, GeraltOpts}]),
  374:               pubsub_tools:subscribe(Bob, Node, [{config, BobOpts}]),
  375: 
  376:               %% 6.3.2 Example 59. Subscriber requests subscription options form
  377:               pubsub_tools:get_subscription_options(Geralt, {node_addr(), NodeName},
  378:                                                     [{expected_result, GeraltOpts}]),
  379:               pubsub_tools:get_subscription_options(Bob, {node_addr(), NodeName},
  380:                                                     [{expected_result, BobOpts}]),
  381: 
  382:               pubsub_tools:delete_node(Alice, Node, [])
  383:       end).
  384: 
  385: subscribe_options_deliver_option_test(Config) ->
  386:     escalus:fresh_story(
  387:       Config,
  388:       [{alice, 1}, {bob, 1}, {geralt, 1}],
  389:       fun(Alice, Bob, Geralt) ->
  390:               Node = pubsub_node(),
  391:               pubsub_tools:create_node(Alice, Node, []),
  392: 
  393:               pubsub_tools:subscribe(Geralt, Node, [{config, [{<<"pubsub#deliver">>, <<"true">>}]}]),
  394:               pubsub_tools:subscribe(Bob, Node, [{config, [{<<"pubsub#deliver">>, <<"false">>}]}]),
  395: 
  396:               pubsub_tools:publish(Alice, <<"item1">>, Node, []),
  397: 
  398:               %% Geralt should receive a notification
  399:               pubsub_tools:receive_item_notification(Geralt, <<"item1">>, Node, [{expected_result, true}]),
  400:               %% Bob should not receive a notification
  401:               [] = escalus:wait_for_stanzas(Bob, 1, 5000),
  402: 
  403:               pubsub_tools:delete_node(Alice, Node, [])
  404:       end).
  405: 
  406: subscribe_options_separate_request_test(Config) ->
  407:     escalus:fresh_story(
  408:       Config,
  409:       [{alice, 1}, {bob, 1}],
  410:       fun(Alice, Bob) ->
  411:               Clients = [Alice, Bob],
  412:               OptionAfterUpdate = {<<"pubsub#deliver">>, <<"false">>},
  413:               {_, NodeName} = Node = pubsub_node(),
  414:               pubsub_tools:create_node(Alice, Node, []),
  415: 
  416:               pubsub_tools:subscribe(Bob, Node, [{config, [{<<"pubsub#deliver">>, <<"true">>}]}]),
  417:               pubsub_tools:subscribe(Alice, Node, []),
  418: 
  419:               %% 6.3.5 Example 68. Subscriber submits completed options form
  420:               [ pubsub_tools:upsert_subscription_options(
  421:                 Client,
  422:                 {node_addr(), NodeName},
  423:                 [{subscription_options,[OptionAfterUpdate]}, {receive_response, true}])
  424:               || Client <- Clients ],
  425: 
  426:               %% 6.3.2 Example 59. Subscriber requests subscription options form
  427:               [ pubsub_tools:get_subscription_options(Client, {node_addr(), NodeName},
  428:                                                     [{expected_result, [OptionAfterUpdate]}])
  429:               || Client <- Clients ],
  430: 
  431:               pubsub_tools:delete_node(Alice, Node, [])
  432:       end).
  433: 
  434: publish_test(Config) ->
  435:     escalus:fresh_story(
  436:       Config,
  437:       [{alice, 1}],
  438:       fun(Alice) ->
  439:               %% Auto-create enabled by default
  440: 
  441:               %% Request:  7.1.1 Ex.99  publish an item with an ItemID
  442:               %% Response: 7.1.2 Ex.100 success
  443:               Node = pubsub_node(),
  444:               pubsub_tools:publish(Alice, <<"item1">>, Node, []),
  445: 
  446:               pubsub_tools:delete_node(Alice, Node, [])
  447:       end).
  448: 
  449: publish_with_max_items_test(Config) ->
  450:     escalus:fresh_story(
  451:       Config,
  452:       [{alice, 1}, {bob, 1}],
  453:       fun(Alice, Bob) ->
  454:               Node = pubsub_node(),
  455:               NodeConfig = [{<<"pubsub#max_items">>, <<"1">>},
  456:                             {<<"pubsub#notify_retract">>, <<"1">>}],
  457:               pubsub_tools:create_node(Alice, Node, [{config, NodeConfig}]),
  458: 
  459:               pubsub_tools:publish(Alice, <<"item1">>, Node, []),
  460: 
  461:               pubsub_tools:subscribe(Bob, Node, []),
  462: 
  463:               %% mod_pubsub:broadcast_step/1 ensures that a publish notification for a new item
  464:               %% would always arrive before a retraction notification for an old item
  465:               pubsub_tools:publish(Alice, <<"item2">>, Node, []),
  466:               pubsub_tools:receive_item_notification(Bob, <<"item2">>, Node, []),
  467:               verify_item_retract(Node, <<"item1">>, escalus:wait_for_stanza(Bob)),
  468: 
  469:               pubsub_tools:delete_node(Alice, Node, [])
  470:       end).
  471: 
  472: publish_with_existing_id_test(Config) ->
  473:     escalus:fresh_story(
  474:       Config,
  475:       [{alice, 1}],
  476:       fun(Alice) ->
  477:               %% Auto-create enabled by default
  478: 
  479:               %% Request:  7.1.1 Ex.99  publish an item with an ItemID
  480:               %% Response: 7.1.2 Ex.100 success
  481:               Node = pubsub_node(),
  482:               pubsub_tools:publish(Alice, <<"item1">>, Node, []),
  483: 
  484:               pubsub_tools:get_item(Alice, Node, <<"item1">>, [{expected_result, [<<"item1">>]}]),
  485: 
  486:               %% Publish an item with the same id in order to update it
  487:               NewEl = #xmlel{name = <<"entry">>, children = [#xmlel{name = <<"new_entry">>}]},
  488:               pubsub_tools:publish(Alice, <<"item1">>, Node, [{with_payload, NewEl}]),
  489:               pubsub_tools:get_item(Alice, Node, <<"item1">>, [{expected_result,
  490:                                                                 [#{id => <<"item1">>,
  491:                                                                    entry => NewEl}]}]),
  492: 
  493: 
  494:               pubsub_tools:delete_node(Alice, Node, [])
  495: 
  496:       end).
  497: 
  498: notify_test(Config) ->
  499:     escalus:fresh_story(
  500:       Config,
  501:       [{alice, 1}, {bob, 2}, {geralt, 2}],
  502:       fun(Alice, Bob1, Bob2, Geralt1, Geralt2) ->
  503:               Node = pubsub_node(),
  504: 
  505:               % It's a quick win for confirming happy path for this option
  506:               % TODO: Extract into separate test case
  507:               NodeConfig = [{<<"pubsub#presence_based_delivery">>, <<"1">>}],
  508: 
  509:               pubsub_tools:create_node(Alice, Node, [{config, NodeConfig}]),
  510:               pubsub_tools:subscribe(Bob1, Node, []),
  511:               pubsub_tools:subscribe(Geralt1, Node, [{jid_type, bare}]),
  512:               pubsub_tools:publish(Alice, <<"item1">>, Node, []),
  513: 
  514:               %% 7.1.2.1 Ex.101 notification with payload
  515:               %%                Note: message has type 'headline' by default
  516: 
  517:               %% Bob subscribed with resource
  518:               pubsub_tools:receive_item_notification(Bob1, <<"item1">>, Node, []),
  519:               escalus_assert:has_no_stanzas(Bob2),
  520: 
  521:               %% Geralt subscribed without resource
  522:               pubsub_tools:receive_item_notification(Geralt1, <<"item1">>, Node, []),
  523:               pubsub_tools:receive_item_notification(Geralt2, <<"item1">>, Node, []),
  524: 
  525:               pubsub_tools:delete_node(Alice, Node, [])
  526:       end).
  527: 
  528: request_all_items_test(Config) ->
  529:     escalus:fresh_story(
  530:       Config,
  531:       [{alice, 1}, {bob, 1}],
  532:       fun(Alice, Bob) ->
  533:               Node = pubsub_node(),
  534:               pubsub_tools:create_node(Alice, Node, []),
  535:               pubsub_tools:publish(Alice, <<"item1">>, Node, []),
  536:               pubsub_tools:publish(Alice, <<"item2">>, Node, []),
  537: 
  538:               %% Request:  6.5.2 Ex.78 subscriber requests all items
  539:               %% Response: 6.5.3 Ex.79 service returns all items
  540:               pubsub_tools:get_all_items(Bob, Node,
  541:                                          [{expected_result, [<<"item2">>, <<"item1">>]}]),
  542:               %% TODO check ordering (although XEP does not specify this)
  543: 
  544:               pubsub_tools:delete_node(Alice, Node, [])
  545:       end).
  546: 
  547: request_particular_item_test(Config) ->
  548:     escalus:fresh_story(
  549:       Config,
  550:       [{alice, 1}, {bob, 1}],
  551:       fun(Alice, Bob) ->
  552:               Node = pubsub_node(),
  553:               pubsub_tools:create_node(Alice, Node, []),
  554:               pubsub_tools:publish(Alice, <<"item1">>, Node, []),
  555:               pubsub_tools:publish(Alice, <<"item2">>, Node, []),
  556: 
  557:               %% Request:  6.5.8 Ex.78 subscriber requests a particular items
  558:               pubsub_tools:get_item(Bob, Node, <<"item1">>, [{expected_result, [<<"item1">>]}]),
  559: 
  560:               pubsub_tools:delete_node(Alice, Node, [])
  561: 
  562:       end).
  563: 
  564: retract_test(Config) ->
  565:     escalus:fresh_story(
  566:       Config,
  567:       [{alice, 1}, {bob, 1}],
  568:       fun(Alice, Bob) ->
  569:               Node = pubsub_node(),
  570:               pubsub_tools:create_node(Alice, Node, []),
  571:               pubsub_tools:publish(Alice, <<"item1">>, Node, []),
  572:               pubsub_tools:publish(Alice, <<"item2">>, Node, []),
  573: 
  574:               %% Request:  7.2.1 Ex.115 Entity deletes an item from a node
  575:               %% Response: 7.2.2 Ex.116 Service replies with success
  576:               pubsub_tools:retract_item(Alice, Node, <<"item1">>, []),
  577:               pubsub_tools:get_all_items(Bob, Node, [{expected_result, [<<"item2">>]}]),
  578: 
  579:               %% Request:  7.2.1 Ex.115 Entity deletes an item from a node
  580:               %% Response: 7.2.2 Ex.116 Service replies with success
  581:               %% Notification: 7.2.2.1 Ex.117 Subscribers are notified of deletion
  582:               pubsub_tools:set_configuration(Alice, Node,
  583:                                              [{<<"pubsub#notify_retract">>, <<"1">>}], []),
  584:               pubsub_tools:subscribe(Bob, Node, []),
  585:               pubsub_tools:retract_item(Alice, Node, <<"item2">>, []),
  586:               verify_item_retract(Node, <<"item2">>, escalus:wait_for_stanza(Bob)),
  587:               pubsub_tools:get_all_items(Bob, Node, [{expected_result, []}]),
  588: 
  589:               pubsub_tools:delete_node(Alice, Node, [])
  590:       end).
  591: 
  592: retract_when_user_goes_offline_test(Config) ->
  593:     escalus:fresh_story(
  594:       Config,
  595:       [{alice, 1}, {bob, 1}],
  596:       fun(Alice, Bob) ->
  597:               Node = pubsub_node(),
  598:               NodeConfig = [{<<"pubsub#purge_offline">>, <<"1">>},
  599:                             {<<"pubsub#publish_model">>, <<"open">>}],
  600:               pubsub_tools:create_node(Alice, Node, [{config, NodeConfig}]),
  601: 
  602:               pubsub_tools:publish(Alice, <<"item1">>, Node, []),
  603:               pubsub_tools:publish(Bob, <<"item2">>, Node, []),
  604:               pubsub_tools:get_all_items(Alice, Node,
  605:                                          [{expected_result, [<<"item2">>, <<"item1">>]}]),
  606: 
  607:               mongoose_helper:logout_user(Config, Bob),
  608: 
  609:               pubsub_tools:get_all_items(Alice, Node, [{expected_result, [<<"item1">>]}]),
  610: 
  611:               pubsub_tools:delete_node(Alice, Node, [])
  612:       end).
  613: 
  614: purge_all_items_test(Config) ->
  615:     escalus:fresh_story(
  616:       Config,
  617:       [{alice, 1}, {bob, 1}],
  618:       fun(Alice, Bob) ->
  619:               Node = pubsub_node(),
  620:               pubsub_tools:create_node(Alice, Node, []),
  621:               pubsub_tools:publish(Alice, <<"item1">>, Node, []),
  622:               pubsub_tools:publish(Alice, <<"item2">>, Node, []),
  623: 
  624:               %% Response: 8.5.3.2 Ex.165 insufficient privileges
  625:               pubsub_tools:purge_all_items(Bob, Node, [{expected_error_type, <<"auth">>}]),
  626: 
  627:               pubsub_tools:get_all_items(Bob, Node,
  628:                                          [{expected_result, [<<"item2">>, <<"item1">>]}]),
  629: 
  630:               %% Request:  8.5.1 Ex.161 owner purges all items from node
  631:               %% Response: 8.5.2 Ex.162 success
  632:               pubsub_tools:purge_all_items(Alice, Node, []),
  633: 
  634:               pubsub_tools:get_all_items(Bob, Node, [{expected_result, []}]),
  635: 
  636:               pubsub_tools:delete_node(Alice, Node, [])
  637:       end).
  638: 
  639: publish_only_retract_items_scope_test(Config) ->
  640:     escalus:fresh_story(
  641:       Config,
  642:       [{alice, 1}, {bob, 1}],
  643:       fun(Alice, Bob) ->
  644:                 Node = pubsub_node(),
  645:                 pubsub_tools:create_node(Alice, Node, []),
  646:                 AffChange = [{Bob, <<"publish-only">>}],
  647:                 pubsub_tools:set_affiliations(Alice, Node, AffChange, []),
  648: 
  649: 
  650:                 pubsub_tools:publish(Bob, <<"item1">>, Node, []),
  651:                 pubsub_tools:publish(Alice, <<"item2">>, Node, []),
  652: 
  653:                 %% Request:  7.2.1 Ex.116 publish-only sends a retract request for his own item
  654:                 %% Response: 7.2.2 Ex.117 success
  655:                 pubsub_tools:retract_item(Bob, Node, <<"item1">>, []),
  656: 
  657:                 %% Request:  7.2.1 Ex.116 publish-only sends a retract request for someone's else item
  658:                 %% Response: 7.2.3.1 Ex.120 insufficient privileges
  659:                 pubsub_tools:retract_item(Bob, Node, <<"item2">>, [{expected_error_type, <<"auth">>}]),
  660:                 pubsub_tools:get_all_items(Alice, Node, [{expected_result, [<<"item2">>]}]),
  661: 
  662:                 pubsub_tools:delete_node(Alice, Node, [])
  663:       end).
  664: 
  665: 
  666: %%--------------------------------------------------------------------
  667: %% Service config
  668: %%--------------------------------------------------------------------
  669: 
  670: max_subscriptions_test(Config) ->
  671:     escalus:fresh_story(
  672:       Config,
  673:       [{alice, 1}, {bob, 1}],
  674:       fun(Alice, Bob) ->
  675:               Node = pubsub_node(),
  676:               pubsub_tools:create_node(Alice, Node, []),
  677: 
  678:               pubsub_tools:subscribe(Alice, Node, []),
  679:               IQError = pubsub_tools:subscribe(Bob, Node, [{expected_error_type, <<"cancel">>}]),
  680:               is_not_allowed_and_closed(IQError),
  681: 
  682:               pubsub_tools:delete_node(Alice, Node, [])
  683:       end).
  684: 
  685: 
  686: %%--------------------------------------------------------------------
  687: %% Node configuration
  688: %%--------------------------------------------------------------------
  689: 
  690: retrieve_default_configuration_test(Config) ->
  691:     escalus:fresh_story(
  692:       Config,
  693:       [{alice, 1}],
  694:       fun(Alice) ->
  695:               NodeAddr = node_addr(),
  696:               pubsub_tools:get_default_configuration(Alice, NodeAddr,
  697:                                                      [{expected_result, default_config()}])
  698:       end).
  699: 
  700: retrieve_configuration_test(Config) ->
  701:     escalus:fresh_story(
  702:       Config,
  703:       [{alice, 1}],
  704:       fun(Alice) ->
  705:               Node = pubsub_node(),
  706:               pubsub_tools:create_node(Alice, Node, []),
  707: 
  708:               NodeConfig = pubsub_tools:get_configuration(Alice, Node, []),
  709:               verify_config_fields(NodeConfig),
  710: 
  711:               pubsub_tools:delete_node(Alice, Node, [])
  712:       end).
  713: 
  714: set_configuration_test(Config) ->
  715:     escalus:fresh_story(
  716:       Config,
  717:       [{alice, 1}],
  718:       fun(Alice) ->
  719:               Node = pubsub_node(),
  720:               pubsub_tools:create_node(Alice, Node, []),
  721: 
  722:               ValidNodeConfig = node_config_for_test(),
  723:               pubsub_tools:set_configuration(Alice, Node, ValidNodeConfig,
  724:                                              [{response_timeout, 10000}]),
  725:               pubsub_tools:get_configuration(Alice, Node, [{expected_result, ValidNodeConfig}]),
  726: 
  727:               pubsub_tools:delete_node(Alice, Node, [])
  728:       end).
  729: 
  730: set_configuration_errors_test(Config) ->
  731:     escalus:fresh_story(
  732:       Config,
  733:       [{alice, 1}],
  734:       fun(Alice) ->
  735:               Node = pubsub_node(),
  736:               pubsub_tools:create_node(Alice, Node, []),
  737: 
  738:               GoodOpts = [{<<"pubsub#notify_config">>, <<"1">>}],
  739:               BadOpts = [{<<"pubsub#notify_config">>, <<"7">>}],
  740: 
  741:               %% Missing <configure> element
  742:               pubsub_tools:set_configuration(Alice, Node, [], [{expected_error_type, <<"modify">>}]),
  743: 
  744:               %% Missing data form
  745:               pubsub_tools:set_configuration(Alice, Node, GoodOpts,
  746:                                              [{modify_request, fun form_helper:remove_forms/1},
  747:                                               {expected_error_type, <<"modify">>}]),
  748: 
  749:               %% Invalid forms
  750:               pubsub_tools:set_configuration(Alice, Node, GoodOpts,
  751:                                              [{modify_request, fun form_helper:remove_form_types/1},
  752:                                               {expected_error_type, <<"modify">>}]),
  753:               pubsub_tools:set_configuration(Alice, Node, GoodOpts,
  754:                                              [{modify_request, fun form_helper:remove_form_ns/1},
  755:                                               {expected_error_type, <<"modify">>}]),
  756:               pubsub_tools:set_configuration(Alice, Node, BadOpts,
  757:                                              [{expected_error_type, <<"modify">>}]),
  758: 
  759:               pubsub_tools:delete_node(Alice, Node, [])
  760:       end).
  761: 
  762: notify_config_test(Config) ->
  763:     escalus:fresh_story(
  764:       Config,
  765:       [{alice, 1}, {bob, 1}],
  766:       fun(Alice, Bob) ->
  767:               Node = pubsub_node(),
  768:               pubsub_tools:create_node(
  769:                 Alice, Node, [{config, [{<<"pubsub#notify_config">>, <<"1">>}]}]),
  770:               pubsub_tools:subscribe(Bob, Node, []),
  771: 
  772:               ConfigChange = [{<<"pubsub#title">>, <<"newtitle">>}],
  773:               pubsub_tools:set_configuration(Alice, Node, ConfigChange, []),
  774:               verify_config_event(Node, ConfigChange, escalus:wait_for_stanza(Bob)),
  775: 
  776:               pubsub_tools:delete_node(Alice, Node, [])
  777:       end).
  778: 
  779: disable_notifications_test(Config) ->
  780:     escalus:fresh_story(
  781:       Config,
  782:       [{alice, 1}, {bob, 1}],
  783:       fun(Alice, Bob) ->
  784:               NodeConfig = [{<<"pubsub#deliver_notifications">>, <<"false">>}],
  785:               Node = pubsub_node(),
  786:               pubsub_tools:create_node(Alice, Node, [{config, NodeConfig}]),
  787: 
  788:               pubsub_tools:subscribe(Bob, Node, []),
  789:               pubsub_tools:publish(Alice, <<"item1">>, Node, []),
  790: 
  791:               %% Notifications disabled
  792:               escalus_assert:has_no_stanzas(Bob),
  793: 
  794:               pubsub_tools:delete_node(Alice, Node, [])
  795:       end).
  796: 
  797: disable_payload_test(Config) ->
  798:     escalus:fresh_story(
  799:       Config,
  800:       [{alice, 1}, {bob, 1}],
  801:       fun(Alice, Bob) ->
  802:               %% Notification-Only Persistent Node, see 4.3, table 4
  803:               NodeConfig = [{<<"pubsub#deliver_payloads">>, <<"false">>}],
  804:               Node = pubsub_node(),
  805:               pubsub_tools:create_node(Alice, Node, [{config, NodeConfig}]),
  806: 
  807:               pubsub_tools:subscribe(Bob, Node, []),
  808:               pubsub_tools:publish(Alice, <<"item1">>, Node, []),
  809: 
  810:               %% Payloads disabled
  811:               pubsub_tools:receive_item_notification(Bob, <<"item1">>,
  812:                                                      Node, [{with_payload, false}]),
  813: 
  814:               pubsub_tools:delete_node(Alice, Node, [])
  815:       end).
  816: 
  817: disable_persist_items_test(Config) ->
  818:     escalus:fresh_story(
  819:       Config,
  820:       [{alice, 1}, {bob, 1}],
  821:       fun(Alice, Bob) ->
  822:               %% Payload-Included Transient Node, see 4.3, table 4
  823:               NodeConfig = [{<<"pubsub#persist_items">>, <<"false">>}],
  824:               Node = pubsub_node(),
  825:               pubsub_tools:create_node(Alice, Node, [{config, NodeConfig}]),
  826: 
  827:               pubsub_tools:subscribe(Bob, Node, []),
  828:               pubsub_tools:publish(Alice, <<"item1">>, Node, []),
  829: 
  830:               %% Notifications should work
  831:               pubsub_tools:receive_item_notification(Bob, <<"item1">>, Node, []),
  832: 
  833:               %% No items should be stored
  834:               pubsub_tools:get_all_items(Bob, Node, [{expected_result, []}]),
  835: 
  836:               pubsub_tools:delete_node(Alice, Node, [])
  837:       end).
  838: 
  839: notify_only_available_users_test(Config) ->
  840:     escalus:fresh_story(
  841:       Config,
  842:       [{alice, 1}, {bob, 1}],
  843:       fun(Alice, Bob) ->
  844:               %% Node notifies only available users
  845:               NodeConfig = [{<<"pubsub#presence_based_delivery">>, <<"true">>}],
  846:               Node = pubsub_node(),
  847:               pubsub_tools:create_node(Alice, Node, [{config, NodeConfig}]),
  848: 
  849:               pubsub_tools:subscribe(Bob, Node, [{jid_type, bare}]),
  850: 
  851:               push_helper:become_unavailable(Bob),
  852: 
  853:               %% Item from node 2 not received (blocked by resource-based delivery)
  854:               pubsub_tools:publish(Alice, <<"item2">>, Node, []),
  855:               escalus_assert:has_no_stanzas(Bob),
  856: 
  857:               pubsub_tools:delete_node(Alice, Node, [])
  858:       end).
  859: 
  860: notify_unavailable_user_test(Config) ->
  861:     escalus:fresh_story(
  862:       Config,
  863:       [{alice, 1}, {bob, 1}],
  864:       fun(Alice, Bob) ->
  865:               Node = pubsub_node(),
  866:               pubsub_tools:create_node(Alice, Node, []),
  867: 
  868:               pubsub_tools:subscribe(Bob, Node, [{jid_type, bare}]),
  869: 
  870:               push_helper:become_unavailable(Bob),
  871: 
  872:               %% Receive item from node 1 (also make sure the presence is processed)
  873:               pubsub_tools:publish(Alice, <<"item1">>, Node, []),
  874: 
  875:               escalus_assert:has_no_stanzas(Bob),
  876:               escalus:send(Bob, escalus_stanza:presence(<<"available">>)),
  877:               pubsub_tools:receive_item_notification(Bob, <<"item1">>, Node, []),
  878: 
  879:               pubsub_tools:delete_node(Alice, Node, [])
  880:       end).
  881: 
  882: send_last_published_item_test(Config) ->
  883:     escalus:fresh_story(
  884:       Config,
  885:       [{alice, 1}, {bob, 1}],
  886:       fun(Alice, Bob) ->
  887:               %% Request:  8.1.3 Ex.136 Request a new node with non-default configuration
  888:               %% Response:       Ex.137 Service replies with success
  889:               NodeConfig = [{<<"pubsub#send_last_published_item">>, <<"on_sub_and_presence">>}],
  890:               Node = pubsub_node(),
  891:               pubsub_tools:create_node(Alice, Node, [{config, NodeConfig}]),
  892: 
  893:               pubsub_tools:publish(Alice, <<"item1">>, Node, []),
  894:               pubsub_tools:publish(Alice, <<"item2">>, Node, []),
  895: 
  896:               %% Note: when Bob subscribes, the last item (item2) is sent to him
  897:               %%       6.1.7 Ex.50 service sends last published item
  898:               %%       This is sent BEFORE the response iq stanza
  899:               pubsub_tools:subscribe(Bob, Node, [{receive_response, false}]),
  900:               pubsub_tools:receive_item_notification(Bob, <<"item2">>, Node, []),
  901:               pubsub_tools:receive_subscribe_response(Bob, Node, []),
  902: 
  903:               pubsub_tools:delete_node(Alice, Node, [])
  904:       end).
  905: 
  906: send_last_published_item_no_items_test(Config) ->
  907:     escalus:fresh_story(
  908:       Config,
  909:       [{alice, 1}, {bob, 1}],
  910:       fun(Alice, Bob) ->
  911:               NodeConfig = [{<<"pubsub#send_last_published_item">>, <<"on_sub_and_presence">>}],
  912:               Node = pubsub_node(),
  913:               pubsub_tools:create_node(Alice, Node, [{config, NodeConfig}]),
  914: 
  915:               %% Note: when Bob subscribes, the last item would is sent to him
  916:               pubsub_tools:subscribe(Bob, Node, [{receive_response, false}]),
  917:               escalus_assert:has_no_stanzas(Bob),
  918:               pubsub_tools:delete_node(Alice, Node, [])
  919:       end).
  920: 
  921: 
  922: 
  923: %%--------------------------------------------------------------------
  924: %% Node affiliations management
  925: %%--------------------------------------------------------------------
  926: 
  927: get_affiliations_test(Config) ->
  928:     escalus:fresh_story(
  929:       Config,
  930:       [{alice, 1}],
  931:       fun(Alice) ->
  932:               Node = pubsub_node(),
  933:               pubsub_tools:create_node(Alice, Node, []),
  934: 
  935:               verify_affiliations(pubsub_tools:get_affiliations(Alice, Node, []),
  936:                                   [{Alice, <<"owner">>}]),
  937: 
  938:               pubsub_tools:delete_node(Alice, Node, [])
  939:       end).
  940: 
  941: add_publisher_and_member_test(Config) ->
  942:     escalus:fresh_story(
  943:       Config,
  944:       [{alice, 1}, {bob, 1}, {kate, 1}],
  945:       fun(Alice, Bob, Kate) ->
  946:               Node = pubsub_node(),
  947:               NodeConfig = [{<<"pubsub#access_model">>, <<"whitelist">>},
  948:                             {<<"pubsub#publish_model">>, <<"publishers">>}],
  949:               pubsub_tools:create_node(Alice, Node, [{config, NodeConfig}]),
  950: 
  951:               pubsub_tools:publish(Bob, <<"item1">>, Node, [{expected_error_type, <<"auth">>}]),
  952:               IQError = pubsub_tools:subscribe(Kate, Node, [{expected_error_type, <<"cancel">>}]),
  953:               is_not_allowed_and_closed(IQError),
  954: 
  955:               AffChange = [{Bob, <<"publisher">>}, {Kate, <<"member">>}],
  956:               pubsub_tools:set_affiliations(Alice, Node, AffChange, []),
  957: 
  958:               pubsub_tools:publish(Kate, <<"nope">>, Node, [{expected_error_type, <<"auth">>}]),
  959:               pubsub_tools:subscribe(Kate, Node, []),
  960:               pubsub_tools:publish(Bob, <<"item1">>, Node, []),
  961:               pubsub_tools:receive_item_notification(Kate, <<"item1">>, Node, []),
  962: 
  963:               pubsub_tools:delete_node(Alice, Node, [])
  964:       end).
  965: 
  966: swap_owners_test(Config) ->
  967:     escalus:fresh_story(
  968:       Config,
  969:       [{alice, 1}, {bob, 1}],
  970:       fun(Alice, Bob) ->
  971:               Node = pubsub_node(),
  972:               pubsub_tools:create_node(Alice, Node, []),
  973: 
  974:               AffChange = [{Bob, <<"owner">>}, {Alice, <<"none">>}],
  975:               pubsub_tools:set_affiliations(Alice, Node, AffChange, []),
  976: 
  977:               pubsub_tools:get_affiliations(Alice, Node, [{expected_error_type, <<"auth">>}]),
  978:               verify_affiliations(pubsub_tools:get_affiliations(Bob, Node, []),
  979:                                   [{Bob, <<"owner">>}]),
  980: 
  981:               pubsub_tools:delete_node(Bob, Node, [])
  982:       end).
  983: 
  984: deny_no_owner_test(Config) ->
  985:     escalus:fresh_story(
  986:       Config,
  987:       [{alice, 1}],
  988:       fun(Alice) ->
  989:               Node = pubsub_node(),
  990:               pubsub_tools:create_node(Alice, Node, []),
  991: 
  992:               AffChange = [{Alice, <<"member">>}],
  993:               IQError = pubsub_tools:set_affiliations(Alice, Node, AffChange,
  994:                                                       [{expected_error_type, <<"modify">>}]),
  995:               verify_returned_affiliation(IQError, Alice, <<"owner">>),
  996: 
  997:               verify_affiliations(pubsub_tools:get_affiliations(Alice, Node, []),
  998:                                   [{Alice, <<"owner">>}]),
  999: 
 1000:               pubsub_tools:delete_node(Alice, Node, [])
 1001:       end).
 1002: 
 1003: %%--------------------------------------------------------------------
 1004: %% Subscriptions management
 1005: %%--------------------------------------------------------------------
 1006: 
 1007: retrieve_user_subscriptions_test(Config) ->
 1008:     escalus:fresh_story(
 1009:       Config,
 1010:       [{alice, 1}, {bob, 1}],
 1011:       fun(Alice, Bob) ->
 1012:               %% Request:  5.6 Ex.20 Retrieve Subscriptions
 1013:               %% Response:     Ex.22 No Subscriptions
 1014:               pubsub_tools:get_user_subscriptions(Bob, node_addr(), [{expected_result, []}]),
 1015: 
 1016:               {_, NodeName} = Node = pubsub_node(),
 1017:               pubsub_tools:create_node(Alice, Node, []),
 1018:               pubsub_tools:subscribe(Bob, Node, []),
 1019: 
 1020:               %% Ex. 21 Service returns subscriptions
 1021:               Sub = [{NodeName, <<"subscribed">>}],
 1022:               pubsub_tools:get_user_subscriptions(Bob, node_addr(), [{expected_result, Sub}]),
 1023: 
 1024:               {_, NodeName2} = Node2 = pubsub_node(),
 1025:               pubsub_tools:create_node(Alice, Node2, []),
 1026:               pubsub_tools:subscribe(Bob, Node2, []),
 1027: 
 1028:               %% Ex. 21 Service returns subscriptions
 1029:               Subs = [{NodeName, <<"subscribed">>}, {NodeName2, <<"subscribed">>}],
 1030:               pubsub_tools:get_user_subscriptions(Bob, node_addr(), [{expected_result, Subs}]),
 1031: 
 1032:               %% Owner not subscribed automatically
 1033:               pubsub_tools:get_user_subscriptions(Alice, node_addr(), [{expected_result, []}]),
 1034: 
 1035:               pubsub_tools:delete_node(Alice, Node, []),
 1036:               pubsub_tools:delete_node(Alice, Node2, [])
 1037:       end).
 1038: 
 1039: retrieve_node_subscriptions_test(Config) ->
 1040:     escalus:fresh_story(
 1041:       Config,
 1042:       [{alice, 1}, {bob, 1}, {geralt, 1}],
 1043:       fun(Alice, Bob, Geralt) ->
 1044:               Node = pubsub_node(),
 1045:               pubsub_tools:create_node(Alice, Node, []),
 1046: 
 1047:               %% Request:  8.8.1.1 Ex.182 Owner requests all subscriptions
 1048:               %% Response: 8.8.1.2 Ex.183 Service returns list of subscriptions (empty yet)
 1049:               pubsub_tools:get_node_subscriptions(Alice, Node, [{expected_result, []}]),
 1050: 
 1051:               %% Response: 8.8.1.3 Ex.185 Entity is not an owner
 1052:               pubsub_tools:get_node_subscriptions(Bob, Node, [{expected_error_type, <<"auth">>}]),
 1053: 
 1054:               pubsub_tools:subscribe(Bob, Node, []),
 1055:               pubsub_tools:subscribe(Geralt, Node, [{jid_type, bare}]),
 1056: 
 1057:               NodeSubs = [{Bob, full, <<"subscribed">>}, {Geralt, bare, <<"subscribed">>}],
 1058:               pubsub_tools:get_node_subscriptions(Alice, Node, [{expected_result, NodeSubs}]),
 1059: 
 1060:               pubsub_tools:delete_node(Alice, Node, [])
 1061:       end).
 1062: 
 1063: modify_node_subscriptions_test(Config) ->
 1064:     escalus:fresh_story(
 1065:       Config,
 1066:       [{alice, 1}, {bob, 1}, {geralt, 1}],
 1067:       fun(Alice, Bob, Geralt) ->
 1068:               Node = pubsub_node(),
 1069:               pubsub_tools:create_node(Alice, Node, []),
 1070: 
 1071:               %% Request:  8.8.2.1 Ex.187 Owner modifies subscriptions
 1072:               %% Response: 8.8.2.2 Ex.183 Service responds with success
 1073:               pubsub_tools:modify_node_subscriptions(
 1074:                 Alice, [{Bob, full, <<"subscribed">>},
 1075:                         {Geralt, bare, <<"subscribed">>}], Node, []),
 1076: 
 1077:               %% 8.8.4 Ex.194 Notify subscribers
 1078:               pubsub_tools:receive_subscription_notification(Bob, <<"subscribed">>, Node, []),
 1079:               pubsub_tools:receive_subscription_notification(Geralt, <<"subscribed">>,
 1080:                                                              Node, [{jid_type, bare}]),
 1081: 
 1082:               Subs = [{Bob, full, <<"subscribed">>}, {Geralt, bare, <<"subscribed">>}],
 1083:               pubsub_tools:get_node_subscriptions(Alice, Node, [{expected_result, Subs}]),
 1084: 
 1085:               %% Response: 8.8.2.3 Ex.190 Entity is not an owner
 1086:               pubsub_tools:modify_node_subscriptions(Bob, [{Geralt, full, <<"subscribed">>}], Node,
 1087:                                                      [{expected_error_type, <<"auth">>}]),
 1088: 
 1089:               %% Remove Bob, add Geralt's full JID
 1090:               pubsub_tools:modify_node_subscriptions(
 1091:                 Alice, [{Bob, full, <<"none">>},
 1092:                         {Geralt, full, <<"subscribed">>}], Node, []),
 1093: 
 1094:               pubsub_tools:receive_subscription_notification(Bob, <<"none">>, Node, []),
 1095:               pubsub_tools:receive_subscription_notification(Geralt, <<"subscribed">>, Node, []),
 1096: 
 1097:               ModSubs = [{Geralt, bare, <<"subscribed">>}, {Geralt, full, <<"subscribed">>}],
 1098:               pubsub_tools:get_node_subscriptions(Alice, Node, [{expected_result, ModSubs}]),
 1099: 
 1100:               pubsub_tools:delete_node(Alice, Node, [])
 1101:       end).
 1102: 
 1103: process_subscription_requests_test(Config) ->
 1104:     escalus:fresh_story(
 1105:       Config,
 1106:       [{alice, 1}, {bob, 1}, {kate, 1}],
 1107:       fun(Alice, Bob, Kate) ->
 1108:               Node = pubsub_node(),
 1109:               NodeConfig = [{<<"pubsub#access_model">>, <<"authorize">>}],
 1110:               pubsub_tools:create_node(Alice, Node, [{config, NodeConfig}]),
 1111: 
 1112:               pubsub_tools:subscribe(Bob, Node, [{subscription, <<"pending">>}]),
 1113:               BobsRequest = pubsub_tools:receive_subscription_request(Alice, Bob, Node, []),
 1114:               pubsub_tools:subscribe(Kate, Node, [{subscription, <<"pending">>}]),
 1115:               KatesRequest = pubsub_tools:receive_subscription_request(Alice, Kate, Node, []),
 1116: 
 1117:               pubsub_tools:submit_subscription_response(Alice, BobsRequest, Node, true, []),
 1118:               pubsub_tools:receive_subscription_notification(Bob, <<"subscribed">>, Node, []),
 1119:               pubsub_tools:submit_subscription_response(Alice, KatesRequest, Node, false, []),
 1120:               pubsub_tools:receive_subscription_notification(Kate, <<"none">>, Node, []),
 1121: 
 1122:               pubsub_tools:publish(Alice, <<"item1">>, Node, []),
 1123:               pubsub_tools:receive_item_notification(Bob, <<"item1">>, Node, []),
 1124:               [] = escalus:peek_stanzas(Kate),
 1125: 
 1126:               pubsub_tools:delete_node(Alice, Node, [])
 1127:       end).
 1128: 
 1129: retrieve_pending_subscription_requests_test(Config) ->
 1130:     escalus:fresh_story(
 1131:       Config,
 1132:       [{alice, 1}, {bob, 1}, {kate, 1}],
 1133:       fun(Alice, Bob, Kate) ->
 1134:               {NodeAddr, NodeName} = Node = pubsub_node(),
 1135:               NodeConfig = [{<<"pubsub#access_model">>, <<"authorize">>}],
 1136:               pubsub_tools:create_node(Alice, Node, [{config, NodeConfig}]),
 1137: 
 1138:               pubsub_tools:subscribe(Bob, Node, [{subscription, <<"pending">>}]),
 1139:               pubsub_tools:receive_subscription_request(Alice, Bob, Node, []),
 1140:               pubsub_tools:subscribe(Kate, Node, [{subscription, <<"pending">>}]),
 1141:               pubsub_tools:receive_subscription_request(Alice, Kate, Node, []),
 1142: 
 1143:               pubsub_tools:get_pending_subscriptions(Alice, NodeAddr, [NodeName], []),
 1144: 
 1145:               %% TODO: XEP requires IQ result to come before the requests
 1146:               Request = pubsub_tools:get_pending_subscriptions(Alice, Node,
 1147:                                                                [{receive_response, false}]),
 1148:               pubsub_tools:receive_subscription_requests(Alice, [Bob, Kate], Node, []),
 1149:               IQRes = escalus:wait_for_stanza(Alice),
 1150:               escalus:assert(is_iq_result, [Request], IQRes),
 1151: 
 1152:               pubsub_tools:delete_node(Alice, Node, [])
 1153:       end).
 1154: 
 1155: retrieve_pending_subscription_requests_errors_test(Config) ->
 1156:     escalus:fresh_story(
 1157:       Config,
 1158:       [{alice, 1}],
 1159:       fun(Alice) ->
 1160:               Node = pubsub_node(),
 1161:               NodeConfig = [{<<"pubsub#access_model">>, <<"authorize">>}],
 1162:               pubsub_tools:create_node(Alice, Node, [{config, NodeConfig}]),
 1163: 
 1164:               %% Invalid form
 1165:               pubsub_tools:get_pending_subscriptions(
 1166:                 Alice, Node, [{modify_request, fun form_helper:remove_form_types/1},
 1167:                               {expected_error_type, <<"modify">>}]),
 1168: 
 1169:               pubsub_tools:delete_node(Alice, Node, [])
 1170:       end).
 1171: 
 1172: %%--------------------------------------------------------------------
 1173: %% Test cases for XEP-0248
 1174: %% Comments in test cases refer to sections is the XEP
 1175: %%--------------------------------------------------------------------
 1176: 
 1177: pubsub_leaf_name() -> pubsub_tools:rand_name(<<"leaf">>).
 1178: pubsub_leaf() -> {node_addr(), pubsub_leaf_name()}.
 1179: 
 1180: create_delete_collection_test(Config) ->
 1181:     escalus:fresh_story(
 1182:       Config,
 1183:       [{alice, 1}],
 1184:       fun(Alice) ->
 1185:               %% Request:  7.1.1 Ex.18 create collection node
 1186:               %% Response:       Ex.19 success
 1187:               %%                        Note: contains node ID although XEP does not require this
 1188:               CollectionConfig = [{<<"pubsub#node_type">>, <<"collection">>}],
 1189:               Node = pubsub_node(),
 1190:               pubsub_tools:create_node(Alice, Node, [{config, CollectionConfig}]),
 1191: 
 1192:               %% Request:  7.3.1 Ex.30 delete collection node
 1193:               %% Response: 7.3.2 Ex.31 success
 1194:               pubsub_tools:delete_node(Alice, Node, [])
 1195:       end).
 1196: 
 1197: subscribe_unsubscribe_collection_test(Config) ->
 1198:     escalus:fresh_story(
 1199:       Config,
 1200:       [{alice, 1}, {bob, 1}],
 1201:       fun(Alice, Bob) ->
 1202:               CollectionConfig = [{<<"pubsub#node_type">>, <<"collection">>}],
 1203:               Node = pubsub_node(),
 1204:               pubsub_tools:create_node(Alice, Node, [{config, CollectionConfig}]),
 1205: 
 1206:               %% Request:  6.1.1 Ex.10 subscribe (no configuration)
 1207:               %% Response: 6.1.2 Ex.12 success
 1208:               pubsub_tools:subscribe(Bob, Node, []),
 1209: 
 1210:               %% Same as XEP-0060
 1211:               pubsub_tools:unsubscribe(Bob, Node, []),
 1212: 
 1213:               pubsub_tools:delete_node(Alice, Node, [])
 1214:       end).
 1215: 
 1216: collection_delete_makes_leaf_parentless(Config) ->
 1217:     escalus:fresh_story(
 1218:       Config,
 1219:       [{alice, 1}],
 1220:       fun(Alice) ->
 1221:               CollectionConfig = [{<<"pubsub#node_type">>, <<"collection">>}],
 1222:               {_, NodeName} = Node = pubsub_node(),
 1223:               pubsub_tools:create_node(Alice, Node, [{config, CollectionConfig}]),
 1224: 
 1225:               %% XEP-0060, 8.1.2, see 16.4.4 for config details
 1226:               NodeConfig = [{<<"pubsub#collection">>, NodeName}],
 1227:               Leaf = pubsub_leaf(),
 1228:               pubsub_tools:create_node(Alice, Leaf, [{config, NodeConfig}]),
 1229: 
 1230:               pubsub_tools:delete_node(Alice, Node, []),
 1231: 
 1232:               % Leaf becomes an orphan
 1233:               NewNodeConfig = pubsub_tools:get_configuration(Alice, Leaf, []),
 1234:               {_, _, []} = lists:keyfind(<<"pubsub#collection">>, 1, NewNodeConfig)
 1235:       end).
 1236: 
 1237: notify_collection_test(Config) ->
 1238:     escalus:fresh_story(
 1239:       Config,
 1240:       [{alice, 1}, {bob, 1}],
 1241:       fun(Alice, Bob) ->
 1242:               CollectionConfig = [{<<"pubsub#node_type">>, <<"collection">>}],
 1243:               {_, NodeName} = Node = pubsub_node(),
 1244:               pubsub_tools:create_node(Alice, Node, [{config, CollectionConfig}]),
 1245: 
 1246:               NodeConfig = [{<<"pubsub#collection">>, NodeName}],
 1247:               Leaf = pubsub_leaf(),
 1248:               pubsub_tools:create_node(Alice, Leaf, [{config, NodeConfig}]),
 1249:               Leaf2 = pubsub_leaf(),
 1250:               pubsub_tools:create_node(Alice, Leaf2, [{config, NodeConfig}]),
 1251:               pubsub_tools:subscribe(Bob, Node, []),
 1252: 
 1253:               %% Publish to leaf nodes, Bob should get notifications
 1254:               %% 5.3.1.1 Ex.5 Subscriber receives a publish notification from a collection
 1255:               pubsub_tools:publish(Alice, <<"item1">>, Leaf, []),
 1256:               pubsub_tools:receive_item_notification(Bob, <<"item1">>, Leaf, [{collection, Node}]),
 1257:               pubsub_tools:publish(Alice, <<"item2">>, Leaf2, []),
 1258:               pubsub_tools:receive_item_notification(Bob, <<"item2">>, Leaf2, [{collection, Node}]),
 1259: 
 1260:               pubsub_tools:delete_node(Alice, Leaf, []),
 1261:               pubsub_tools:delete_node(Alice, Leaf2, []),
 1262:               pubsub_tools:delete_node(Alice, Node, [])
 1263:       end).
 1264: 
 1265: notify_collection_leaf_and_item_test(Config) ->
 1266:     escalus:fresh_story(
 1267:       Config,
 1268:       [{alice, 1}, {bob, 1}],
 1269:       fun(Alice, Bob) ->
 1270:               CollectionConfig = [{<<"pubsub#node_type">>, <<"collection">>}],
 1271:               {_, NodeName} = Node = pubsub_node(),
 1272:               pubsub_tools:create_node(Alice, Node, [{config, CollectionConfig}]),
 1273: 
 1274:               %% Subscribe before creating the leaf node
 1275:               pubsub_tools:subscribe(Bob, Node, []),
 1276:               NodeConfig = [{<<"pubsub#collection">>, NodeName}],
 1277:               Leaf = pubsub_leaf(),
 1278:               pubsub_tools:create_node(Alice, Leaf, [{config, NodeConfig}]),
 1279: 
 1280:               %% Bob should get a notification for the leaf node creation
 1281:               %% 5.3.1.2 Ex.6 Subscriber receives a creation notification from a collection
 1282:               pubsub_tools:receive_node_creation_notification(Bob, Leaf, []),
 1283: 
 1284:               %% Publish to leaf node, Bob should get notified
 1285:               pubsub_tools:publish(Alice, <<"item1">>, Leaf, []),
 1286:               pubsub_tools:receive_item_notification(Bob, <<"item1">>, Leaf, [{collection, Node}]),
 1287: 
 1288:               pubsub_tools:delete_node(Alice, Leaf, []),
 1289:               pubsub_tools:delete_node(Alice, Node, [])
 1290:       end).
 1291: 
 1292: notify_collection_bare_jid_test(Config) ->
 1293:     escalus:fresh_story(
 1294:       Config,
 1295:       [{alice, 1}, {bob, 2}, {geralt, 2}],
 1296:       fun(Alice, Bob1, Bob2, Geralt1, Geralt2) ->
 1297:               CollectionConfig = [{<<"pubsub#node_type">>, <<"collection">>}],
 1298:               {_, NodeName} = Node = pubsub_node(),
 1299:               pubsub_tools:create_node(Alice, Node, [{config, CollectionConfig}]),
 1300: 
 1301:               NodeConfig = [{<<"pubsub#collection">>, NodeName}],
 1302:               Leaf = pubsub_leaf(),
 1303:               pubsub_tools:create_node(Alice, Leaf, [{config, NodeConfig}]),
 1304:               pubsub_tools:subscribe(Bob1, Node, []),
 1305:               pubsub_tools:subscribe(Geralt1, Node, [{jid_type, bare}]),
 1306:               pubsub_tools:publish(Alice, <<"item1">>, Leaf, []),
 1307: 
 1308:               %% Bob subscribed with resource
 1309:               pubsub_tools:receive_item_notification(Bob1, <<"item1">>, Leaf, [{collection, Node}]),
 1310:               escalus_assert:has_no_stanzas(Bob2),
 1311: 
 1312:               %% Geralt subscribed without resource
 1313:               pubsub_tools:receive_item_notification(Geralt1, <<"item1">>, Leaf,
 1314:                                                      [{collection, Node}]),
 1315:               pubsub_tools:receive_item_notification(Geralt2, <<"item1">>, Leaf,
 1316:                                                      [{collection, Node}]),
 1317: 
 1318:               pubsub_tools:delete_node(Alice, Leaf, []),
 1319:               pubsub_tools:delete_node(Alice, Node, [])
 1320:       end).
 1321: 
 1322: notify_collection_and_leaf_test(Config) ->
 1323:     escalus:fresh_story(
 1324:       Config,
 1325:       [{alice, 1}, {bob, 1}, {geralt, 1}],
 1326:       fun(Alice, Bob, Geralt) ->
 1327:               CollectionConfig = [{<<"pubsub#node_type">>, <<"collection">>}],
 1328:               {_, NodeName} = Node = pubsub_node(),
 1329:               pubsub_tools:create_node(Alice, Node, [{config, CollectionConfig}]),
 1330: 
 1331:               NodeConfig = [{<<"pubsub#collection">>, NodeName}],
 1332:               Leaf = pubsub_leaf(),
 1333:               pubsub_tools:create_node(Alice, Leaf, [{config, NodeConfig}]),
 1334:               pubsub_tools:subscribe(Bob, Node, []),
 1335:               pubsub_tools:subscribe(Geralt, Leaf, []),
 1336: 
 1337:               %% Publish to leaf nodes, Bob and Geralt should get notifications
 1338:               pubsub_tools:publish(Alice, <<"item1">>, Leaf, []),
 1339:               pubsub_tools:receive_item_notification(Bob, <<"item1">>, Leaf,
 1340:                                                      [{collection, Node}]),
 1341:               pubsub_tools:receive_item_notification(Geralt, <<"item1">>, Leaf,
 1342:                                                      [no_collection_shim]),
 1343: 
 1344:               pubsub_tools:delete_node(Alice, Leaf, []),
 1345:               pubsub_tools:delete_node(Alice, Node, [])
 1346:       end).
 1347: 
 1348: notify_collection_and_leaf_same_user_test(Config) ->
 1349:     escalus:fresh_story(
 1350:       Config,
 1351:       [{alice, 1}, {bob, 1}],
 1352:       fun(Alice, Bob) ->
 1353:               CollectionConfig = [{<<"pubsub#node_type">>, <<"collection">>}],
 1354:               {_, NodeName} = Node = pubsub_node(),
 1355:               pubsub_tools:create_node(Alice, Node, [{config, CollectionConfig}]),
 1356: 
 1357:               NodeConfig = [{<<"pubsub#collection">>, NodeName}],
 1358:               Leaf = pubsub_leaf(),
 1359:               pubsub_tools:create_node(Alice, Leaf, [{config, NodeConfig}]),
 1360:               pubsub_tools:subscribe(Bob, Node, []),
 1361:               pubsub_tools:subscribe(Bob, Leaf, []),
 1362: 
 1363:               %% Bob should get only one notification
 1364:               pubsub_tools:publish(Alice, <<"item1">>, Leaf, []),
 1365:               pubsub_tools:receive_item_notification(Bob, <<"item1">>, Leaf, [{collection, Node}]),
 1366:               escalus_assert:has_no_stanzas(Bob),
 1367: 
 1368:               pubsub_tools:delete_node(Alice, Leaf, []),
 1369:               pubsub_tools:delete_node(Alice, Node, [])
 1370:       end).
 1371: 
 1372: notify_collections_with_same_leaf_test(Config) ->
 1373:     escalus:fresh_story(
 1374:       Config,
 1375:       [{alice, 1}, {bob, 1}, {geralt, 1}],
 1376:       fun(Alice, Bob, Geralt) ->
 1377:               CollectionConfig = [{<<"pubsub#node_type">>, <<"collection">>}],
 1378:               {_, CollectionName1} = Collection1 = pubsub_node(),
 1379:               pubsub_tools:create_node(Alice, Collection1, [{config, CollectionConfig}]),
 1380:               {_, CollectionName2} = Collection2 = pubsub_node(),
 1381:               pubsub_tools:create_node(Alice, Collection2, [{config, CollectionConfig}]),
 1382: 
 1383:               LeafConfig = [{<<"pubsub#collection">>, <<"text-multi">>,
 1384:                              [CollectionName1, CollectionName2]}],
 1385:               Leaf = pubsub_leaf(),
 1386:               pubsub_tools:create_node(Alice, Leaf, [{config, LeafConfig}]),
 1387:               pubsub_tools:subscribe(Bob, Collection1, []),
 1388:               pubsub_tools:subscribe(Geralt, Collection2, []),
 1389: 
 1390:               %% Publish to leaf node, Bob and Geralt should get notifications
 1391:               pubsub_tools:publish(Alice, <<"item1">>, Leaf, []),
 1392:               pubsub_tools:receive_item_notification(Bob, <<"item1">>, Leaf,
 1393:                                                      [{collection, Collection1}]),
 1394:               pubsub_tools:receive_item_notification(Geralt, <<"item1">>, Leaf,
 1395:                                                      [{collection, Collection2}]),
 1396: 
 1397:               pubsub_tools:delete_node(Alice, Leaf, []),
 1398:               pubsub_tools:delete_node(Alice, Collection1, []),
 1399:               pubsub_tools:delete_node(Alice, Collection2, [])
 1400:       end).
 1401: 
 1402: notify_nested_collections_test(Config) ->
 1403:     escalus:fresh_story(
 1404:       Config,
 1405:       [{alice, 1}, {bob, 1}, {geralt, 1}],
 1406:       fun(Alice, Bob, Geralt) ->
 1407:               TopCollectionConfig = [{<<"pubsub#node_type">>, <<"collection">>}],
 1408:               {_, TopCollectionName} = TopCollection = pubsub_node(),
 1409:               pubsub_tools:create_node(Alice, TopCollection, [{config, TopCollectionConfig}]),
 1410: 
 1411:               MiddleCollectionConfig = [
 1412:                                         {<<"pubsub#node_type">>, <<"collection">>},
 1413:                                         {<<"pubsub#collection">>, TopCollectionName}
 1414:                                        ],
 1415:               {_, MiddleCollectionName} = MiddleCollection = pubsub_node(),
 1416:               pubsub_tools:create_node(Alice, MiddleCollection, [{config, MiddleCollectionConfig}]),
 1417: 
 1418:               LeafConfig = [{<<"pubsub#collection">>, MiddleCollectionName}],
 1419:               Leaf = pubsub_leaf(),
 1420:               pubsub_tools:create_node(Alice, Leaf, [{config, LeafConfig}]),
 1421:               pubsub_tools:subscribe(Bob, MiddleCollection, []),
 1422:               pubsub_tools:subscribe(Geralt, TopCollection, []),
 1423: 
 1424:               %% Publish to leaf node, Bob and Geralt should get notifications
 1425:               pubsub_tools:publish(Alice, <<"item1">>, Leaf, []),
 1426:               pubsub_tools:receive_item_notification(Bob, <<"item1">>, Leaf,
 1427:                                                      [{collection, MiddleCollection}]),
 1428:               pubsub_tools:receive_item_notification(Geralt, <<"item1">>, Leaf,
 1429:                                                      [{collection, TopCollection}]),
 1430: 
 1431:               pubsub_tools:delete_node(Alice, Leaf, []),
 1432:               pubsub_tools:delete_node(Alice, MiddleCollection, []),
 1433:               pubsub_tools:delete_node(Alice, TopCollection, [])
 1434:       end).
 1435: 
 1436: retrieve_subscriptions_collection_test(Config) ->
 1437:     escalus:fresh_story(
 1438:       Config,
 1439:       [{alice, 1}, {bob, 1}],
 1440:       fun(Alice, Bob) ->
 1441:               CollectionConfig = [{<<"pubsub#node_type">>, <<"collection">>}],
 1442:               {_, NodeName} = Node = pubsub_node(),
 1443:               pubsub_tools:create_node(Alice, Node, [{config, CollectionConfig}]),
 1444: 
 1445:               NodeConfig = [{<<"pubsub#collection">>, NodeName}],
 1446:               {_, LeafName} = Leaf = pubsub_leaf(),
 1447:               pubsub_tools:create_node(Alice, Leaf, [{config, NodeConfig}]),
 1448:               Leaf2 = pubsub_leaf(),
 1449:               pubsub_tools:create_node(Alice, Leaf2, [{config, NodeConfig}]),
 1450:               pubsub_tools:subscribe(Bob, Node, []),
 1451:               pubsub_tools:subscribe(Bob, Leaf, []),
 1452: 
 1453:               % Only the nodes for which subscriptions were made should be returned
 1454:               Subs = [{LeafName, <<"subscribed">>}, {NodeName, <<"subscribed">>}],
 1455:               pubsub_tools:get_user_subscriptions(Bob, node_addr(), [{expected_result, Subs}]),
 1456: 
 1457:               pubsub_tools:delete_node(Alice, Leaf, []),
 1458:               pubsub_tools:delete_node(Alice, Leaf2, []),
 1459:               pubsub_tools:delete_node(Alice, Node, [])
 1460:       end).
 1461: 
 1462: discover_top_level_nodes_test(Config) ->
 1463:     escalus:fresh_story(
 1464:       Config,
 1465:       [{alice, 1}, {bob, 1}],
 1466:       fun(Alice, Bob) ->
 1467:               % This one is visible at top level
 1468: 
 1469:               CollectionConfig = [{<<"pubsub#node_type">>, <<"collection">>}],
 1470:               {_, NodeName} = Node = pubsub_node(),
 1471:               pubsub_tools:create_node(Alice, Node, [{config, CollectionConfig}]),
 1472: 
 1473:               % This one is not
 1474: 
 1475:               LeafConfig = [{<<"pubsub#collection">>, NodeName}],
 1476:               {_, LeafName} = Leaf = pubsub_leaf(),
 1477:               pubsub_tools:create_node(Alice, Leaf, [{config, LeafConfig}]),
 1478: 
 1479:               % This one is visible, as it is not associated with any collection
 1480:               {_, CollectionlessName} = Collectionless = pubsub_node(),
 1481:               pubsub_tools:create_node(Alice, Collectionless, []),
 1482: 
 1483:               %% Discover top-level nodes, only the collection expected
 1484:               pubsub_tools:discover_nodes(Bob, node_addr(),
 1485:                                           [{expected_result, [NodeName, CollectionlessName,
 1486:                                                               {no, LeafName}]}]),
 1487: 
 1488:               pubsub_tools:delete_node(Alice, Leaf, []),
 1489:               pubsub_tools:delete_node(Alice, Node, []),
 1490:               pubsub_tools:delete_node(Alice, Collectionless, [])
 1491:       end).
 1492: 
 1493: discover_child_nodes_test(Config) ->
 1494:     escalus:fresh_story(
 1495:       Config,
 1496:       [{alice, 1}, {bob, 1}],
 1497:       fun(Alice, Bob) ->
 1498:               %% Try to get children of a non-existing node
 1499:               {_, NodeName} = Node = pubsub_node(),
 1500:               pubsub_tools:discover_nodes(Bob, Node, [{expected_error_type, <<"cancel">>}]),
 1501: 
 1502:               CollectionConfig = [{<<"pubsub#node_type">>, <<"collection">>}],
 1503:               pubsub_tools:create_node(Alice, Node, [{config, CollectionConfig}]),
 1504: 
 1505:               pubsub_tools:discover_nodes(Bob, Node, [{expected_result, [{no, NodeName}]}]),
 1506: 
 1507:               NodeConfig = [{<<"pubsub#collection">>, NodeName}],
 1508:               {_, LeafName} = Leaf = pubsub_leaf(),
 1509:               pubsub_tools:create_node(Alice, Leaf, [{config, NodeConfig}]),
 1510:               {_, LeafName2} = Leaf2 = pubsub_leaf(),
 1511:               pubsub_tools:create_node(Alice, Leaf2, [{config, NodeConfig}]),
 1512: 
 1513:               %% Request:  5.2.1 Ex.11 Entity requests child nodes
 1514:               %% Response: 5.2.2 Ex.12 Service returns child nodes
 1515:               pubsub_tools:discover_nodes(Bob, Node, [{expected_result, [LeafName, LeafName2]}]),
 1516: 
 1517:               pubsub_tools:delete_node(Alice, Leaf, []),
 1518:               pubsub_tools:delete_node(Alice, Leaf2, []),
 1519:               pubsub_tools:delete_node(Alice, Node, [])
 1520:       end).
 1521: 
 1522: request_all_items_leaf_test(Config) ->
 1523:     escalus:fresh_story(
 1524:       Config,
 1525:       [{alice, 1}, {bob, 1}],
 1526:       fun(Alice, Bob) ->
 1527:               CollectionConfig = [{<<"pubsub#node_type">>, <<"collection">>}],
 1528:               {_, NodeName} = Node = pubsub_node(),
 1529:               pubsub_tools:create_node(Alice, Node, [{config, CollectionConfig}]),
 1530: 
 1531:               NodeConfig = [{<<"pubsub#collection">>, NodeName}],
 1532:               Leaf = pubsub_leaf(),
 1533:               pubsub_tools:create_node(Alice, Leaf, [{config, NodeConfig}]),
 1534:               Leaf2 = pubsub_leaf(),
 1535:               pubsub_tools:create_node(Alice, Leaf2, [{config, NodeConfig}]),
 1536: 
 1537:               pubsub_tools:publish(Alice, <<"item1">>, Leaf, []),
 1538:               pubsub_tools:publish(Alice, <<"item2">>, Leaf2, []),
 1539: 
 1540:               %% Request items from leaf nodes - as described in XEP-0060
 1541:               pubsub_tools:get_all_items(Bob, Leaf, [{expected_result, [<<"item1">>]}]),
 1542:               pubsub_tools:get_all_items(Bob, Leaf2, [{expected_result, [<<"item2">>]}]),
 1543: 
 1544:               %% NOTE: This is not implemented yet
 1545:               %% Request:  6.2.1 Ex.15 Subscriber requests all items on a collection
 1546:               %% Response: 6.2.2 Ex.16 Service returns items on leaf nodes
 1547:               %%pubsub_tools:get_all_items(Bob, Node,
 1548:               %%                           [{expected_result, [<<"item2">>, <<"item1">>]}]),
 1549: 
 1550:               pubsub_tools:delete_node(Alice, Leaf, []),
 1551:               pubsub_tools:delete_node(Alice, Leaf2, []),
 1552:               pubsub_tools:delete_node(Alice, Node, [])
 1553:       end).
 1554: 
 1555: %%--------------------------------------------------------------------
 1556: %% Collections config
 1557: %%--------------------------------------------------------------------
 1558: 
 1559: disable_notifications_leaf_test(Config) ->
 1560:     escalus:fresh_story(
 1561:       Config,
 1562:       [{alice, 1}, {bob, 1}],
 1563:       fun(Alice, Bob) ->
 1564:               CollectionConfig = [{<<"pubsub#node_type">>, <<"collection">>}],
 1565:               {_, NodeName} = Node = pubsub_node(),
 1566:               pubsub_tools:create_node(Alice, Node, [{config, CollectionConfig}]),
 1567: 
 1568:               NodeConfig = [{<<"pubsub#deliver_notifications">>, <<"false">>},
 1569:                             {<<"pubsub#collection">>, NodeName}],
 1570:               Leaf = pubsub_leaf(),
 1571:               pubsub_tools:create_node(Alice, Leaf, [{config, NodeConfig}]),
 1572: 
 1573:               pubsub_tools:subscribe(Bob, Node, []),
 1574:               pubsub_tools:publish(Alice, <<"item1">>, Leaf, []),
 1575: 
 1576:               %% Notifications disabled
 1577:               escalus_assert:has_no_stanzas(Bob),
 1578: 
 1579:               pubsub_tools:delete_node(Alice, Leaf, []),
 1580:               pubsub_tools:delete_node(Alice, Node, [])
 1581:       end).
 1582: 
 1583: disable_payload_leaf_test(Config) ->
 1584:     escalus:fresh_story(
 1585:       Config,
 1586:       [{alice, 1}, {bob, 1}],
 1587:       fun(Alice, Bob) ->
 1588:               CollectionConfig = [{<<"pubsub#node_type">>, <<"collection">>}],
 1589:               {_, NodeName} = Node = pubsub_node(),
 1590:               pubsub_tools:create_node(Alice, Node, [{config, CollectionConfig}]),
 1591: 
 1592:               NodeConfig = [{<<"pubsub#deliver_payloads">>, <<"false">>},
 1593:                             {<<"pubsub#collection">>, NodeName}],
 1594:               Leaf = pubsub_leaf(),
 1595:               pubsub_tools:create_node(Alice, Leaf, [{config, NodeConfig}]),
 1596: 
 1597:               pubsub_tools:subscribe(Bob, Node, []),
 1598:               pubsub_tools:publish(Alice, <<"item1">>, Leaf, []),
 1599: 
 1600:               %% Payloads disabled
 1601:               pubsub_tools:receive_item_notification(Bob, <<"item1">>, Leaf,
 1602:                                                      [{with_payload, false}, {collection, Node}]),
 1603: 
 1604:               pubsub_tools:delete_node(Alice, Leaf, []),
 1605:               pubsub_tools:delete_node(Alice, Node, [])
 1606:       end).
 1607: 
 1608: disable_persist_items_leaf_test(Config) ->
 1609:     escalus:fresh_story(
 1610:       Config,
 1611:       [{alice, 1}, {bob, 1}],
 1612:       fun(Alice, Bob) ->
 1613:               CollectionConfig = [{<<"pubsub#node_type">>, <<"collection">>}],
 1614:               {_, NodeName} = Node = pubsub_node(),
 1615:               pubsub_tools:create_node(Alice, Node, [{config, CollectionConfig}]),
 1616: 
 1617:               NodeConfig = [{<<"pubsub#persist_items">>, <<"false">>},
 1618:                             {<<"pubsub#collection">>, NodeName}],
 1619:               Leaf = pubsub_leaf(),
 1620:               pubsub_tools:create_node(Alice, Leaf, [{config, NodeConfig}]),
 1621: 
 1622:               pubsub_tools:subscribe(Bob, Node, []),
 1623:               pubsub_tools:publish(Alice, <<"item1">>, Leaf, []),
 1624: 
 1625:               %% Notifications should work
 1626:               pubsub_tools:receive_item_notification(Bob, <<"item1">>, Leaf, [{collection, Node}]),
 1627: 
 1628:               %% No items should be stored
 1629:               pubsub_tools:get_all_items(Bob, Leaf, [{expected_result, []}]),
 1630: 
 1631:               pubsub_tools:delete_node(Alice, Leaf, []),
 1632:               pubsub_tools:delete_node(Alice, Node, [])
 1633:       end).
 1634: 
 1635: %%--------------------------------------------------------------------
 1636: %% Debug calls tests
 1637: %%--------------------------------------------------------------------
 1638: 
 1639: debug_get_items_test(Config) ->
 1640:     escalus:fresh_story(
 1641:       Config,
 1642:       [{alice, 1}],
 1643:       fun(Alice) ->
 1644:               {NodeAddr, NodeName} = Node = pubsub_node(),
 1645:               pubsub_tools:create_node(Alice, Node, []),
 1646:               pubsub_tools:publish(Alice, <<"item1">>, Node, []),
 1647:               pubsub_tools:publish(Alice, <<"item2">>, Node, []),
 1648: 
 1649:               Items = rpc(mim(), mod_pubsub, get_items, [NodeAddr, NodeName]),
 1650:               % We won't bother with importing records etc...
 1651:               2 = length(Items),
 1652: 
 1653:               {error, _} = rpc(mim(), mod_pubsub, get_items, [NodeAddr, <<"no_such_node_here">>]),
 1654: 
 1655:               pubsub_tools:delete_node(Alice, Node, [])
 1656:       end).
 1657: 
 1658: debug_get_item_test(Config) ->
 1659:     escalus:fresh_story(
 1660:       Config,
 1661:       [{alice, 1}],
 1662:       fun(Alice) ->
 1663:               {NodeAddr, NodeName} = Node = pubsub_node(),
 1664:               pubsub_tools:create_node(Alice, Node, []),
 1665:               pubsub_tools:publish(Alice, <<"item1">>, Node, []),
 1666:               pubsub_tools:publish(Alice, <<"item2">>, Node, []),
 1667: 
 1668:               Item = rpc(mim(), mod_pubsub, get_item, [NodeAddr, NodeName, <<"item2">>]),
 1669:               % We won't bother with importing records etc...
 1670:               {<<"item2">>, _} = element(2, Item),
 1671: 
 1672:               {error, _} = rpc(mim(), mod_pubsub, get_item, [NodeAddr, NodeName, <<"itemX">>]),
 1673: 
 1674:               pubsub_tools:delete_node(Alice, Node, [])
 1675:       end).
 1676: %%--------------------------------------------------------------------
 1677: %% Tests for unsupported features  - excluded from suite
 1678: %%--------------------------------------------------------------------
 1679: 
 1680: disable_payload_and_persist_test(Config) ->
 1681:     escalus:fresh_story(
 1682:       Config,
 1683:       [{alice, 1}, {bob, 1}],
 1684:       fun(Alice, Bob) ->
 1685:               %% Notification-Only Transient Node, see 4.3, table 4
 1686:               NodeConfig = [{<<"pubsub#deliver_payloads">>, <<"false">>},
 1687:                             {<<"pubsub#persist_items">>, <<"false">>}],
 1688:               Node = pubsub_node(),
 1689:               pubsub_tools:create_node(Alice, Node, [{config, NodeConfig}]),
 1690: 
 1691:               pubsub_tools:subscribe(Bob, Node, []),
 1692: 
 1693:               %% Response  7.1.3 Ex.112 attempt to publish payload to transient notification node
 1694:               %%                   Expected error of type 'modify'
 1695:               pubsub_tools:publish(Alice, <<"item1">>, Node,
 1696:                                    [{expected_error_type, <<"modify">>}]),
 1697: 
 1698:               %% Publish without payload should succeed
 1699:               pubsub_tools:publish(Alice, <<"item2">>, Node, [{with_payload, false}]),
 1700: 
 1701:               %% Notifications should work
 1702:               pubsub_tools:receive_item_notification(Bob, <<"item1">>, Node, []),
 1703: 
 1704:               %% No items should be stored
 1705:               pubsub_tools:get_all_items(Bob, Node, [{expected_result, []}]),
 1706: 
 1707:               %% No more notifications
 1708:               escalus_assert:has_no_stanzas(Bob),
 1709: 
 1710:               pubsub_tools:delete_node(Alice, Node, [])
 1711:       end).
 1712: 
 1713: disable_delivery_test(Config) ->
 1714:     escalus:fresh_story(
 1715:       Config,
 1716:       [{alice, 1}, {bob, 1}],
 1717:       fun(Alice, Bob) ->
 1718:               Node = pubsub_node(),
 1719:               pubsub_tools:create_node(Alice, Node, []),
 1720: 
 1721:               %% Request: 6.3.7 Ex.71 Subscribe and configure
 1722:               %%                Ex.72 Success
 1723:               SubscrConfig = [{<<"pubsub#deliver">>, <<"false">>}],
 1724:               pubsub_tools:subscribe(Bob, Node, [{config, SubscrConfig}]),
 1725: 
 1726:               pubsub_tools:publish(Alice, <<"item1">>, Node, []),
 1727: 
 1728:               %% Notifications disabled
 1729:               escalus_assert:has_no_stanzas(Bob),
 1730: 
 1731:               pubsub_tools:delete_node(Alice, Node, [])
 1732:       end).
 1733: %%-----------------------------------------------------------------
 1734: %% pubsub_item_publisher_option
 1735: %%-----------------------------------------------------------------
 1736: 
 1737: get_item_with_publisher_option_test(Config) ->
 1738:     escalus:fresh_story(
 1739:       Config,
 1740:       [{alice, 1}],
 1741:       fun(Alice) ->
 1742:               Node = pubsub_node(),
 1743:               pubsub_tools:create_node(Alice, Node, []),
 1744: 
 1745:               pubsub_tools:publish(Alice, <<"item1">>, Node, []),
 1746: 
 1747:               PublisherJID =  escalus_utils:jid_to_lower(escalus_client:full_jid(Alice)),
 1748:               pubsub_tools:get_item(Alice, Node, <<"item1">>,
 1749:                                     [{expected_result, [#{id => <<"item1">>,
 1750:                                                           publisher => PublisherJID}]}]),
 1751:               pubsub_tools:delete_node(Alice, Node, [])
 1752:       end).
 1753: 
 1754: receive_item_notification_with_publisher_option_test(Config) ->
 1755:     escalus:fresh_story(
 1756:       Config,
 1757:       [{alice, 1}, {bob, 1}],
 1758:       fun(Alice, Bob) ->
 1759:               Node = pubsub_node(),
 1760:               pubsub_tools:create_node(Alice, Node, []),
 1761: 
 1762:               pubsub_tools:subscribe(Bob, Node, []),
 1763:               pubsub_tools:publish(Alice, <<"item1">>, Node, []),
 1764: 
 1765: 
 1766:               PublisherJID =  escalus_utils:jid_to_lower(escalus_client:full_jid(Alice)),
 1767:               pubsub_tools:receive_item_notification(Bob, #{id => <<"item1">>,
 1768:                                                             publisher => PublisherJID}, Node, []),
 1769: 
 1770:               pubsub_tools:delete_node(Alice, Node, [])
 1771:       end).
 1772: 
 1773: %%-----------------------------------------------------------------
 1774: %% hometree - specific
 1775: %%-----------------------------------------------------------------
 1776: 
 1777: can_create_node_with_existing_parent_path(Config) ->
 1778:     escalus:fresh_story(
 1779:       Config,
 1780:       [{alice, 1}],
 1781:       fun(Alice) ->
 1782:               {Parent, Node} = path_node_and_parent(Alice, pubsub_node()),
 1783:               pubsub_tools:create_node(Alice, Parent, []),
 1784:               pubsub_tools:create_node(Alice, Node, []),
 1785: 
 1786:               pubsub_tools:delete_node(Alice, Node, []),
 1787:               pubsub_tools:delete_node(Alice, Parent, [])
 1788:       end).
 1789: 
 1790: cant_create_node_with_missing_parent_path(Config) ->
 1791:     escalus:fresh_story(
 1792:       Config,
 1793:       [{alice, 1}],
 1794:       fun(Alice) ->
 1795:               {_Parent, Node} = path_node_and_parent(Alice, pubsub_node()),
 1796:               pubsub_tools:create_node(Alice, Node, [{expected_error_type, <<"auth">>}])
 1797:       end).
 1798: 
 1799: disco_node_children_by_path_prefix(Config) ->
 1800:     escalus:fresh_story(
 1801:       Config,
 1802:       [{alice, 1}, {bob, 1}],
 1803:       fun(Alice, Bob) ->
 1804:               %% Try to get children of a non-existing node
 1805:               {Parent, {_, NodeName} = Node} = path_node_and_parent(Alice, pubsub_node()),
 1806:               pubsub_tools:discover_nodes(Bob, Parent, [{expected_error_type, <<"cancel">>}]),
 1807: 
 1808:               pubsub_tools:create_node(Alice, Parent, []),
 1809: 
 1810:               pubsub_tools:discover_nodes(Bob, Parent, [{expected_result, []}]),
 1811: 
 1812:               pubsub_tools:create_node(Alice, Node, []),
 1813: 
 1814:               %% Request:  5.2.1 Ex.11 Entity requests child nodes
 1815:               %% Response: 5.2.2 Ex.12 Service returns child nodes
 1816:               pubsub_tools:discover_nodes(Bob, Parent, [{expected_result, [NodeName]}]),
 1817: 
 1818:               pubsub_tools:delete_node(Alice, Node, []),
 1819:               pubsub_tools:delete_node(Alice, Parent, [])
 1820:       end).
 1821: 
 1822: deleting_parent_path_deletes_children(Config) ->
 1823:     escalus:fresh_story(
 1824:       Config,
 1825:       [{alice, 1}],
 1826:       fun(Alice) ->
 1827:               {{_, ParentName} = Parent, {_, NodeName} = Node}
 1828:               = path_node_and_parent(Alice, pubsub_node()),
 1829: 
 1830:               pubsub_tools:create_node(Alice, Parent, []),
 1831:               pubsub_tools:create_node(Alice, Node, []),
 1832: 
 1833:               pubsub_tools:delete_node(Alice, Parent, []),
 1834: 
 1835:               pubsub_tools:discover_nodes(Alice, node_addr(),
 1836:                                           [{expected_result, [{no, ParentName}, {no, NodeName}]}]),
 1837:               pubsub_tools:discover_nodes(Alice, Node, [{expected_error_type, <<"cancel">>}])
 1838:       end).
 1839: 
 1840: %%-----------------------------------------------------------------
 1841: %% Helpers
 1842: %%-----------------------------------------------------------------
 1843: 
 1844: path_node_and_parent(Client, {NodeAddr, NodeName}) ->
 1845:     %% TODO: Add proper JID stringprepping to escalus!!!
 1846:     JID = escalus_ejabberd:rpc(jid, from_binary, [escalus_client:short_jid(Client)]),
 1847:     {LUser, LServer, _} = escalus_ejabberd:rpc(jid, to_lower, [JID]),
 1848:     Prefix = <<"/home/", LServer/binary, "/", LUser/binary>>,
 1849:     {{NodeAddr, Prefix}, {NodeAddr, <<Prefix/binary, "/", NodeName/binary>>}}.
 1850: 
 1851: required_modules(ExtraOpts) ->
 1852:     Opts = maps:merge(#{backend => mongoose_helper:mnesia_or_rdbms_backend(),
 1853:                         host => subhost_pattern("pubsub.@HOST@")},
 1854:                       ExtraOpts),
 1855:     [{mod_pubsub, config_parser_helper:mod_config(mod_pubsub, Opts)}].
 1856: 
 1857: verify_config_fields(NodeConfig) ->
 1858:     ValidFields = [
 1859:                    {<<"FORM_TYPE">>, <<"hidden">>},
 1860:                    {<<"pubsub#title">>, <<"text-single">>},
 1861:                    {<<"pubsub#deliver_notifications">>, <<"boolean">>},
 1862:                    {<<"pubsub#deliver_payloads">>, <<"boolean">>},
 1863:                    {<<"pubsub#notify_config">>, <<"boolean">>},
 1864:                    {<<"pubsub#notify_delete">>, <<"boolean">>},
 1865:                    {<<"pubsub#notify_retract">>, <<"boolean">>},
 1866: % not supported yet                   {<<"pubsub#notify_sub">>, <<"boolean">>},
 1867:                    {<<"pubsub#persist_items">>, <<"boolean">>},
 1868:                    {<<"pubsub#max_items">>, <<"text-single">>},
 1869: % not supported yet                   {<<"pubsub#item_expire">>, <<"text-single">>},
 1870:                    {<<"pubsub#subscribe">>, <<"boolean">>},
 1871:                    {<<"pubsub#access_model">>, <<"list-single">>},
 1872:                    {<<"pubsub#roster_groups_allowed">>, <<"list-multi">>},
 1873:                    {<<"pubsub#publish_model">>, <<"list-single">>},
 1874:                    {<<"pubsub#purge_offline">>, <<"boolean">>},
 1875:                    {<<"pubsub#max_payload_size">>, <<"text-single">>},
 1876:                    {<<"pubsub#send_last_published_item">>, <<"list-single">>},
 1877:                    {<<"pubsub#presence_based_delivery">>, <<"boolean">>},
 1878:                    {<<"pubsub#notification_type">>, <<"list-single">>},
 1879:                    {<<"pubsub#type">>, <<"text-single">>},
 1880: % not supported yet                   {<<"pubsub#dataform_xslt">>, <<"text-single">>}
 1881: % not supported yet                   {<<"pubsub#node_type">>, undef},
 1882: % not supported yet                   {<<"pubsub#children">>, undef},
 1883:                    {<<"pubsub#collection">>, <<"text-multi">>}
 1884:                   ],
 1885:     [] =
 1886:     lists:foldl(fun({Var, Type}, Fields) ->
 1887:                         {{value, {_, Type, _}, NewFields}, _}
 1888:                         = {lists:keytake(Var, 1, Fields), Var},
 1889:                         NewFields
 1890:                 end, NodeConfig, ValidFields).
 1891: 
 1892: node_config_for_test() ->
 1893:     [
 1894:      {<<"pubsub#title">>, <<"TARDIS">>},
 1895:      {<<"pubsub#deliver_notifications">>, <<"1">>},
 1896:      {<<"pubsub#deliver_payloads">>, <<"1">>},
 1897:      {<<"pubsub#notify_config">>, <<"0">>},
 1898:      {<<"pubsub#notify_delete">>, <<"1">>},
 1899:      {<<"pubsub#notify_retract">>, <<"1">>},
 1900:      % Not supported yet                   {<<"pubsub#notify_sub">>, <<"boolean">>},
 1901:      {<<"pubsub#persist_items">>, <<"0">>},
 1902:      {<<"pubsub#max_items">>, <<"10">>},
 1903:      % Not supported yet: {<<"pubsub#item_expire">>, <<"text-single">>},
 1904:      {<<"pubsub#subscribe">>, <<"0">>},
 1905:      {<<"pubsub#access_model">>, <<"presence">>},
 1906:      % TODO: Verify with test case: {<<"pubsub#roster_groups_allowed">>, <<"list-multi">>},
 1907:      {<<"pubsub#publish_model">>, <<"publishers">>},
 1908:      {<<"pubsub#purge_offline">>, <<"1">>},
 1909:      {<<"pubsub#max_payload_size">>, <<"24601">>},
 1910:      {<<"pubsub#send_last_published_item">>, <<"on_sub">>},
 1911:      {<<"pubsub#presence_based_delivery">>, <<"1">>},
 1912:      {<<"pubsub#notification_type">>, <<"normal">>},
 1913:      {<<"pubsub#type">>, <<"urn:mim">>}
 1914:      % Not supported yet: {<<"pubsub#dataform_xslt">>, <<"text-single">>}
 1915:      % Not supported yet: {<<"pubsub#node_type">>, undef},
 1916:      % Not supported yet: {<<"pubsub#children">>, undef},
 1917:      % Covered by collection tests: {<<"pubsub#collection">>, <<"text-multi">>}
 1918:     ].
 1919: 
 1920: default_config() ->
 1921:     [{<<"pubsub#deliver_payloads">>, <<"1">>},
 1922:      {<<"pubsub#deliver_notifications">>, <<"1">>},
 1923:      {<<"pubsub#notify_config">>, <<"0">>},
 1924:      {<<"pubsub#notify_delete">>, <<"0">>},
 1925:      {<<"pubsub#notify_retract">>, <<"0">>},
 1926:      {<<"pubsub#persist_items">>, <<"1">>},
 1927:      {<<"pubsub#title">>, <<>>},
 1928:      {<<"pubsub#max_items">>, <<"10">>},
 1929:      {<<"pubsub#subscribe">>, <<"1">>},
 1930:      {<<"pubsub#access_model">>, <<"open">>},
 1931:      {<<"pubsub#roster_groups_allowed">>, []},
 1932:      {<<"pubsub#publish_model">>, <<"publishers">>},
 1933:      {<<"pubsub#purge_offline">>, <<"0">>},
 1934:      {<<"pubsub#notification_type">>, <<"headline">>},
 1935:      {<<"pubsub#max_payload_size">>, <<"60000">>},
 1936:      {<<"pubsub#send_last_published_item">>, <<"never">>},
 1937:      {<<"pubsub#presence_based_delivery">>, <<"0">>},
 1938:      {<<"pubsub#type">>, <<>>},
 1939:      {<<"pubsub#collection">>, []}].
 1940: 
 1941: verify_item_retract({NodeAddr, NodeName}, ItemId, Stanza) ->
 1942:     escalus:assert(is_message, Stanza),
 1943:     NodeAddr = exml_query:attr(Stanza, <<"from">>),
 1944: 
 1945:     [#xmlel{ attrs = [{<<"xmlns">>, ?NS_PUBSUB_EVENT}] } = Event]
 1946:     = exml_query:subelements(Stanza, <<"event">>),
 1947: 
 1948:     [#xmlel{ attrs = [{<<"node">>, NodeName}] } = Items]
 1949:     = exml_query:subelements(Event, <<"items">>),
 1950: 
 1951:     [#xmlel{ attrs = [{<<"id">>, ItemId}] }] = exml_query:subelements(Items, <<"retract">>).
 1952: 
 1953: verify_config_event({NodeAddr, NodeName}, ConfigChange, Stanza) ->
 1954:     escalus:assert(is_message, Stanza),
 1955:     NodeAddr = exml_query:attr(Stanza, <<"from">>),
 1956: 
 1957:     [#xmlel{ attrs = [{<<"xmlns">>, ?NS_PUBSUB_EVENT}] } = Event]
 1958:     = exml_query:subelements(Stanza, <<"event">>),
 1959: 
 1960:     [#xmlel{ attrs = [{<<"node">>, NodeName}] } = ConfigEl]
 1961:     = exml_query:subelements(Event, <<"configuration">>),
 1962: 
 1963:     Fields = exml_query:paths(ConfigEl, [{element, <<"x">>},
 1964:                                          {element, <<"field">>}]),
 1965: 
 1966:     Opts = [ {exml_query:attr(F, <<"var">>),
 1967:               exml_query:path(F, [{element, <<"value">>}, cdata])} || F <- Fields ],
 1968: 
 1969:     true = lists:all(fun({K, V}) ->
 1970:                              {K, V} =:= lists:keyfind(K, 1, Opts)
 1971:                      end, ConfigChange).
 1972: 
 1973: verify_affiliations(Affiliations, ValidAffiliations) ->
 1974:     NormalisedValidAffiliations
 1975:     = lists:sort([ {escalus_utils:jid_to_lower(escalus_client:short_jid(Client)), Aff}
 1976:                    || {Client, Aff} <- ValidAffiliations ]),
 1977:     NormalisedValidAffiliations = lists:sort(Affiliations).
 1978: 
 1979: verify_returned_affiliation(IQError, User, Aff) ->
 1980:     UserJid = escalus_utils:jid_to_lower(escalus_utils:get_short_jid(User)),
 1981:     QPath = [{element, <<"pubsub">>},
 1982:              {element, <<"affiliations">>},
 1983:              {element, <<"affiliation">>}],
 1984:     [AffEl] = exml_query:paths(IQError, QPath),
 1985:     UserJid = exml_query:attr(AffEl, <<"jid">>),
 1986:     Aff = exml_query:attr(AffEl, <<"affiliation">>).
 1987: 
 1988: is_not_allowed_and_closed(IQError) ->
 1989:     ?NS_STANZA_ERRORS = exml_query:path(IQError, [{element, <<"error">>},
 1990:                                                   {element, <<"not-allowed">>},
 1991:                                                   {attr, <<"xmlns">>}]),
 1992:     ?NS_PUBSUB_ERRORS = exml_query:path(IQError, [{element, <<"error">>},
 1993:                                                   {element, <<"closed-node">>},
 1994:                                                   {attr, <<"xmlns">>}]).