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