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">>}]).