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