1: %%============================================================================== 2: %% Copyright 2012 Erlang Solutions Ltd. 3: %% 4: %% Licensed under the Apache License, Version 2.0 (the "License"); 5: %% you may not use this file except in compliance with the License. 6: %% You may obtain a copy of the License at 7: %% 8: %% http://www.apache.org/licenses/LICENSE-2.0 9: %% 10: %% Unless required by applicable law or agreed to in writing, software 11: %% distributed under the License is distributed on an "AS IS" BASIS, 12: %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13: %% See the License for the specific language governing permissions and 14: %% limitations under the License. 15: %%============================================================================== 16: -module(mam_SUITE). 17: 18: %% CT callbacks 19: -export([all/0, 20: groups/0, 21: suite/0, 22: init_per_suite/1, 23: end_per_suite/1, 24: init_per_group/2, 25: end_per_group/2, 26: init_per_testcase/2, 27: end_per_testcase/2]). 28: 29: %% Tests 30: -export([no_elements/1, 31: only_stanzaid/1, 32: same_stanza_id/1, 33: muc_no_elements/1, 34: muc_only_stanzaid/1, 35: mam_service_discovery/1, 36: muc_service_discovery/1, 37: simple_archive_request/1, 38: simple_archive_request_for_the_receiver/1, 39: text_search_query_fails_if_disabled/1, 40: text_search_is_not_available/1, 41: simple_text_search_request/1, 42: long_text_search_request/1, 43: text_search_is_available/1, 44: muc_message_with_stanzaid/1, 45: retract_muc_message/1, 46: retract_muc_message_on_stanza_id/1, 47: retract_wrong_muc_message/1, 48: muc_text_search_request/1, 49: muc_archive_request/1, 50: muc_multiple_devices/1, 51: muc_protected_message/1, 52: muc_deny_protected_room_access/1, 53: muc_allow_access_to_owner/1, 54: muc_delete_x_user_in_anon_rooms/1, 55: muc_show_x_user_to_moderators_in_anon_rooms/1, 56: muc_show_x_user_for_your_own_messages_in_anon_rooms/1, 57: range_archive_request/1, 58: range_archive_request_not_empty/1, 59: limit_archive_request/1, 60: prefs_set_request/1, 61: retrieve_form_fields/1, 62: prefs_set_cdata_request/1, 63: query_get_request/1, 64: pagination_first5/1, 65: pagination_last5/1, 66: pagination_offset5/1, 67: pagination_first0/1, 68: pagination_last0/1, 69: pagination_offset5_max0/1, 70: pagination_before10/1, 71: pagination_after10/1, 72: pagination_simple_before10/1, 73: pagination_last_after_id5/1, 74: pagination_last_after_id5_before_id11/1, 75: pagination_empty_rset/1, 76: pagination_first5_opt_count/1, 77: pagination_first25_opt_count_all/1, 78: pagination_last5_opt_count/1, 79: pagination_last25_opt_count_all/1, 80: pagination_offset5_opt_count/1, 81: pagination_offset5_opt_count_all/1, 82: server_returns_item_not_found_for_before_filter_with_nonexistent_id/1, 83: server_returns_item_not_found_for_after_filter_with_nonexistent_id/1, 84: %% complete_flag_cases tests 85: before_complete_false_last5/1, 86: before_complete_false_before10/1, 87: before_complete_true_before1/1, 88: before_complete_true_before5/1, 89: before_complete_true_before6/1, 90: after_complete_false_first_page/1, 91: after_complete_false_after2/1, 92: after_complete_false_after9/1, 93: after_complete_true_after10/1, 94: after_complete_true_after11/1, 95: archived/1, 96: message_with_stanzaid/1, 97: retract_message/1, 98: retract_message_on_stanza_id/1, 99: retract_wrong_message/1, 100: filter_forwarded/1, 101: offline_message/1, 102: nostore_hint/1, 103: querying_for_all_messages_with_jid/1, 104: muc_querying_for_all_messages/1, 105: muc_querying_for_all_messages_with_jid/1, 106: muc_light_simple/1, 107: muc_light_shouldnt_modify_pm_archive/1, 108: muc_light_stored_in_pm_if_allowed_to/1, 109: muc_light_chat_markers_are_archived_if_enabled/1, 110: muc_light_chat_markers_are_not_archived_if_disabled/1, 111: messages_filtered_when_prefs_default_policy_is_always/1, 112: messages_filtered_when_prefs_default_policy_is_never/1, 113: messages_filtered_when_prefs_default_policy_is_roster/1, 114: run_set_and_get_prefs_cases/1, 115: check_user_exist/1, 116: metric_incremented_on_archive_request/1, 117: metric_incremented_when_store_message/1, 118: metrics_incremented_for_async_pools/1, 119: archive_chat_markers/1, 120: dont_archive_chat_markers/1, 121: save_unicode_messages/1, 122: unicode_messages_can_be_extracted/1, 123: stanza_id_is_appended_to_carbons/1]). 124: 125: -import(distributed_helper, [mim/0, 126: require_rpc_nodes/1, 127: subhost_pattern/1, 128: rpc/4]). 129: 130: -import(muc_helper, 131: [muc_host/0, 132: room_address/1, room_address/2, 133: stanza_muc_enter_room/2, 134: stanza_to_room/2]). 135: 136: -import(mam_helper, 137: [rpc_apply/3, 138: get_prop/2, 139: is_riak_enabled/1, 140: is_cassandra_enabled/1, 141: is_elasticsearch_enabled/1, 142: is_mam_possible/1, 143: respond_iq/1, 144: print_configuration_not_supported/2, 145: start_alice_room/1, 146: destroy_room/1, 147: send_muc_rsm_messages/1, 148: send_rsm_messages/1, 149: clean_archives/1, 150: mam04_props/0, 151: mam06_props/0, 152: bootstrap_archive/1, 153: muc_bootstrap_archive/1, 154: start_alice_protected_room/1, 155: start_alice_anonymous_room/1, 156: maybe_wait_for_archive/1, 157: stanza_archive_request/2, 158: stanza_text_search_archive_request/3, 159: stanza_date_range_archive_request_not_empty/3, 160: wait_archive_respond/1, 161: wait_for_complete_archive_response/3, 162: assert_respond_size/2, 163: assert_respond_query_id/3, 164: parse_result_iq/1, 165: nick_to_jid/2, 166: stanza_filtered_by_jid_request/2, 167: nick/1, 168: respond_messages/1, 169: parse_forwarded_message/1, 170: login_send_presence/2, 171: assert_only_one_of_many_is_equal/2, 172: add_nostore_hint/1, 173: assert_not_stored/2, 174: has_x_user_element/1, 175: stanza_date_range_archive_request/1, 176: make_iso_time/1, 177: stanza_retrieve_form_fields/2, 178: stanza_limit_archive_request/1, 179: rsm_send/3, 180: stanza_page_archive_request/3, 181: wait_empty_rset/2, 182: wait_message_range/3, 183: message_id/2, 184: wait_message_range/5, 185: stanza_prefs_set_request/4, 186: stanza_prefs_get_request/1, 187: stanza_query_get_request/1, 188: parse_prefs_result_iq/1, 189: mam_ns_binary/0, 190: mam_ns_binary_v04/0, 191: mam_ns_binary_v06/0, 192: retract_ns/0, 193: retract_tombstone_ns/0, 194: make_alice_and_bob_friends/2, 195: run_prefs_case/6, 196: prefs_cases2/0, 197: default_policy/1, 198: get_all_messages/2, 199: parse_messages/1, 200: run_set_and_get_prefs_case/4, 201: muc_light_host/0, 202: host_type/0, 203: set_wait_for_parallel_writer/2 204: ]). 205: 206: -import(muc_light_helper, 207: [given_muc_light_room/3, 208: when_muc_light_message_is_sent/4, 209: then_muc_light_message_is_received_by/2, 210: when_muc_light_affiliations_are_set/3, 211: then_muc_light_affiliations_are_received_by/2, 212: when_archive_query_is_sent/3, 213: then_archive_response_is/3]). 214: 215: -import(domain_helper, [domain/0]). 216: 217: -include("mam_helper.hrl"). 218: -include_lib("escalus/include/escalus.hrl"). 219: -include_lib("escalus/include/escalus_xmlns.hrl"). 220: -include_lib("common_test/include/ct.hrl"). 221: -include_lib("exml/include/exml_stream.hrl"). 222: 223: %%-------------------------------------------------------------------- 224: %% Suite configuration 225: %%-------------------------------------------------------------------- 226: 227: 228: 229: configurations() -> 230: case ct_helper:is_ct_running() of 231: true -> 232: configurations_for_running_ct(); 233: false -> 234: all_configurations() 235: end. 236: 237: %% Called by test-runner for autocompletion 238: all_configurations() -> 239: cassandra_configs(true) 240: ++ rdbms_configs(true) 241: ++ riak_configs(true) 242: ++ elasticsearch_configs(true). 243: 244: configurations_for_running_ct() -> 245: cassandra_configs(is_cassandra_enabled(host_type())) 246: ++ rdbms_configs(mongoose_helper:is_rdbms_enabled(host_type())) 247: ++ riak_configs(is_riak_enabled(host_type())) 248: ++ elasticsearch_configs(is_elasticsearch_enabled(host_type())). 249: 250: rdbms_configs(true) -> 251: [rdbms, 252: rdbms_simple, 253: rdbms_async_pool, 254: rdbms_mnesia, 255: rdbms_async_cache, 256: rdbms_cache, 257: rdbms_mnesia_cache, 258: rdbms_mnesia_muc_cache 259: ]; 260: rdbms_configs(_) -> 261: []. 262: 263: riak_configs(true) -> 264: [riak_timed_yz_buckets]; 265: riak_configs(_) -> 266: []. 267: 268: cassandra_configs(true) -> 269: [cassandra]; 270: cassandra_configs(_) -> 271: []. 272: 273: elasticsearch_configs(true) -> 274: [elasticsearch]; 275: elasticsearch_configs(_) -> 276: []. 277: 278: basic_group_names() -> 279: [ 280: mam_all, 281: chat_markers, 282: muc_all, 283: muc_light, 284: prefs_cases, 285: impl_specific, 286: disabled_text_search, 287: disabled_retraction 288: ]. 289: 290: all() -> 291: Reasons = 292: case ct_helper:is_ct_running() of 293: true -> 294: case is_mam_possible(host_type()) of 295: false -> [require_rdbms]; 296: true -> [] 297: end; 298: false -> 299: [] 300: end, 301: case Reasons of 302: [] -> 303: tests(); 304: [_|_] -> 305: {skip, Reasons} 306: end. 307: 308: tests() -> 309: [{group, full_group(C, G)} 310: || C <- configurations(), G <- basic_group_names(), 311: not is_skipped(C, G)]. 312: 313: groups() -> 314: [{full_group(C, G), Props, Tests} 315: || C <- configurations(), {G, Props, Tests} <- basic_groups(), 316: not is_skipped(C, G)]. 317: 318: is_skipped(_, _) -> 319: false. 320: 321: basic_groups() -> 322: [ 323: {mam_all, [parallel], 324: [{mam_metrics, [], mam_metrics_cases()}, 325: {mam04, [parallel], mam_cases() ++ [retrieve_form_fields] ++ text_search_cases()}, 326: {mam06, [parallel], mam_cases() ++ stanzaid_cases() ++ retract_cases()}, 327: {nostore, [parallel], nostore_cases()}, 328: {archived, [parallel], archived_cases()}, 329: {configurable_archiveid, [], configurable_archiveid_cases()}, 330: {rsm_all, [], %% not parallel, because we want to limit concurrency 331: [ 332: %% Functions mod_mam_utils:make_fin_element_v03/5 and make_fin_element/5 333: %% are almost the same, so don't need to test all versions of 334: %% MAM protocol with complete_flag_cases. 335: %% 336: %% We need a separate group for complete_flag_cases, 337: %% because there should not be a lot of cases running 338: %% using parallel_story with the same user. 339: %% Otherwise there would be a lot of presences sent between devices. 340: {rsm04, [parallel], rsm_cases()}, 341: {rsm04_comp, [parallel], complete_flag_cases()}, 342: {with_rsm04, [parallel], with_rsm_cases()}]}]}, 343: {muc_all, [parallel], 344: [{muc04, [parallel], muc_cases() ++ muc_text_search_cases()}, 345: {muc06, [parallel], muc_cases() ++ muc_stanzaid_cases() ++ muc_retract_cases()}, 346: {muc_configurable_archiveid, [], muc_configurable_archiveid_cases()}, 347: {muc_rsm_all, [parallel], 348: [{muc_rsm04, [parallel], muc_rsm_cases()}]}]}, 349: {muc_light, [], muc_light_cases()}, 350: {prefs_cases, [parallel], prefs_cases()}, 351: {impl_specific, [], impl_specific()}, 352: {disabled_text_search, [], 353: [{mam04, [], disabled_text_search_cases()}]}, 354: {chat_markers, [parallel], 355: [{mam04, [parallel], chat_markers_cases()}]}, 356: {disabled_retraction, [], 357: [{mam06, [parallel], disabled_retract_cases() ++ 358: [mam_service_discovery]}]}, 359: {muc_disabled_retraction, [], 360: [{muc06, [parallel], disabled_muc_retract_cases() ++ 361: [muc_service_discovery]}]} 362: ]. 363: 364: chat_markers_cases() -> 365: [archive_chat_markers, 366: dont_archive_chat_markers]. 367: 368: mam_metrics_cases() -> 369: [metric_incremented_on_archive_request, 370: metric_incremented_when_store_message]. 371: 372: mam_cases() -> 373: [mam_service_discovery, 374: simple_archive_request, 375: simple_archive_request_for_the_receiver, 376: range_archive_request, 377: range_archive_request_not_empty, 378: limit_archive_request, 379: querying_for_all_messages_with_jid, 380: unicode_messages_can_be_extracted 381: ]. 382: 383: text_search_cases() -> 384: [ 385: simple_text_search_request, 386: long_text_search_request, 387: text_search_is_available, 388: save_unicode_messages 389: ]. 390: 391: disabled_text_search_cases() -> 392: [ 393: text_search_is_not_available, 394: text_search_query_fails_if_disabled 395: ]. 396: 397: muc_text_search_cases() -> 398: [ 399: muc_text_search_request 400: ]. 401: 402: archived_cases() -> 403: [archived, 404: filter_forwarded, 405: metrics_incremented_for_async_pools 406: ]. 407: 408: stanzaid_cases() -> 409: [message_with_stanzaid, 410: stanza_id_is_appended_to_carbons]. 411: 412: retract_cases() -> 413: [retract_message, 414: retract_wrong_message]. 415: 416: disabled_retract_cases() -> 417: [retract_message]. 418: 419: nostore_cases() -> 420: [offline_message, 421: nostore_hint]. 422: 423: muc_cases() -> 424: [muc_service_discovery, 425: muc_archive_request, 426: muc_multiple_devices, 427: muc_protected_message, 428: muc_deny_protected_room_access, 429: muc_allow_access_to_owner, 430: muc_delete_x_user_in_anon_rooms, 431: muc_show_x_user_to_moderators_in_anon_rooms, 432: muc_show_x_user_for_your_own_messages_in_anon_rooms, 433: muc_querying_for_all_messages, 434: muc_querying_for_all_messages_with_jid 435: ]. 436: 437: muc_stanzaid_cases() -> 438: [muc_message_with_stanzaid]. 439: 440: muc_retract_cases() -> 441: [retract_muc_message, 442: retract_muc_message_on_stanza_id, 443: retract_wrong_muc_message]. 444: 445: disabled_muc_retract_cases() -> 446: [retract_muc_message]. 447: 448: muc_configurable_archiveid_cases() -> 449: [ 450: muc_no_elements, 451: muc_only_stanzaid 452: ]. 453: 454: configurable_archiveid_cases() -> 455: [no_elements, 456: only_stanzaid, 457: same_stanza_id, 458: retract_message_on_stanza_id 459: ]. 460: 461: muc_light_cases() -> 462: [ 463: muc_light_simple, 464: muc_light_shouldnt_modify_pm_archive, 465: muc_light_stored_in_pm_if_allowed_to, 466: muc_light_chat_markers_are_archived_if_enabled, 467: muc_light_chat_markers_are_not_archived_if_disabled 468: ]. 469: 470: muc_rsm_cases() -> 471: rsm_cases(). 472: 473: with_rsm_cases() -> 474: rsm_cases(). 475: 476: rsm_cases() -> 477: [pagination_first5, 478: pagination_last5, 479: pagination_offset5, 480: pagination_first0, 481: pagination_last0, 482: pagination_offset5_max0, 483: pagination_before10, 484: pagination_after10, 485: pagination_empty_rset, 486: %% Border cases 487: pagination_last_after_id5, 488: pagination_last_after_id5_before_id11, 489: %% Simple cases 490: pagination_simple_before10, 491: %% opt_count cases 492: pagination_first5_opt_count, 493: pagination_last5_opt_count, 494: pagination_offset5_opt_count, 495: %% opt_count cases with all messages on the page 496: pagination_first25_opt_count_all, 497: pagination_last25_opt_count_all, 498: pagination_offset5_opt_count_all, 499: %% item_not_found response for nonexistent message ID in before/after filters 500: server_returns_item_not_found_for_before_filter_with_nonexistent_id, 501: server_returns_item_not_found_for_after_filter_with_nonexistent_id]. 502: 503: complete_flag_cases() -> 504: [before_complete_false_last5, 505: before_complete_false_before10, 506: before_complete_true_before1, 507: before_complete_true_before5, 508: before_complete_true_before6, 509: after_complete_false_first_page, 510: after_complete_false_after2, 511: after_complete_false_after9, 512: after_complete_true_after10, 513: after_complete_true_after11]. 514: 515: prefs_cases() -> 516: [prefs_set_request, 517: prefs_set_cdata_request, 518: query_get_request, 519: messages_filtered_when_prefs_default_policy_is_always, 520: messages_filtered_when_prefs_default_policy_is_never, 521: messages_filtered_when_prefs_default_policy_is_roster, 522: run_set_and_get_prefs_cases]. 523: 524: impl_specific() -> 525: [check_user_exist]. 526: 527: suite() -> 528: require_rpc_nodes([mim]) ++ escalus:suite(). 529: 530: init_per_suite(Config) -> 531: muc_helper:load_muc(), 532: mam_helper:prepare_for_suite( 533: increase_limits( 534: delete_users([{escalus_user_db, {module, escalus_ejabberd}} 535: | escalus:init_per_suite(Config)]))). 536: 537: end_per_suite(Config) -> 538: muc_helper:unload_muc(), 539: %% Next function creates a lot of sessions... 540: escalus_fresh:clean(), 541: %% and this function kicks them without waiting... 542: mongoose_helper:kick_everyone(), 543: %% so we don't have sessions anymore and other tests will not fail 544: mongoose_helper:restore_config(Config), 545: escalus:end_per_suite(Config). 546: 547: user_names() -> 548: [alice, bob, kate, carol]. 549: 550: create_users(Config) -> 551: escalus:create_users(Config, escalus:get_users(user_names())). 552: 553: delete_users(Config) -> 554: escalus:delete_users(Config, escalus:get_users(user_names())). 555: 556: increase_limits(Config) -> 557: Config1 = mongoose_helper:backup_and_set_config(Config, increased_limits()), 558: rpc_apply(shaper_srv, reset_all_shapers, [host_type()]), 559: Config1. 560: 561: increased_limits() -> 562: #{[shaper, mam_shaper] => #{max_rate => 10000}, 563: [shaper, normal] => #{max_rate => 10000000}, 564: [shaper, fast] => #{max_rate => 10000000}, 565: [{access, host_type()}, max_user_sessions] => [#{acl => all, value => 10000}]}. 566: 567: init_per_group(mam04, Config) -> 568: [{props, mam04_props()}|Config]; 569: init_per_group(mam06, Config) -> 570: [{props, mam06_props()}|Config]; 571: 572: init_per_group(rsm_all, Config) -> 573: Config1 = escalus_fresh:create_users(Config, [{N, 1} || N <- user_names()]), 574: send_rsm_messages(Config1); 575: init_per_group(rsm04, Config) -> 576: [{props, mam04_props()}|Config]; 577: init_per_group(rsm04_comp, Config) -> 578: [{props, mam04_props()}|Config]; 579: init_per_group(with_rsm04, Config) -> 580: [{props, mam04_props()}, {with_rsm, true}|Config]; 581: 582: init_per_group(nostore, Config) -> 583: Config; 584: init_per_group(archived, Config) -> 585: Config; 586: init_per_group(mam_metrics, Config) -> 587: Config; 588: init_per_group(muc04, Config) -> 589: [{props, mam04_props()}, {with_rsm, true}|Config]; 590: init_per_group(muc06, Config) -> 591: [{props, mam06_props()}, {with_rsm, true}|Config]; 592: 593: init_per_group(muc_configurable_archiveid, Config) -> 594: dynamic_modules:save_modules(host_type(), Config); 595: init_per_group(configurable_archiveid, Config) -> 596: dynamic_modules:save_modules(host_type(), Config); 597: 598: init_per_group(muc_rsm_all, Config) -> 599: Config1 = escalus_fresh:create_users(Config, [{N, 1} || N <- user_names()]), 600: Config2 = start_alice_room(Config1), 601: Config3 = send_muc_rsm_messages(Config2), 602: [{muc_rsm, true} | Config3]; 603: init_per_group(muc_rsm04, Config) -> 604: [{props, mam04_props()}|Config]; 605: 606: init_per_group(Group, ConfigIn) -> 607: C = configuration(Group), 608: B = basic_group(Group), 609: try init_modules(C, B, ConfigIn) of 610: skip -> 611: {skip, print_configuration_not_supported(C, B)}; 612: Config0 -> 613: ct:pal("Init per group ~p; configuration ~p; basic group ~p", 614: [Group, C, B]), 615: Config01 = dynamic_modules:save_modules(host_type(), Config0), 616: Config1 = do_init_per_group(C, Config01), 617: [{basic_group, B}, {configuration, C} | init_state(C, B, Config1)] 618: catch Class:Reason:Stacktrace -> 619: ct:pal("Failed to start configuration=~p basic_group=~p", 620: [C, B]), 621: erlang:raise(Class, Reason, Stacktrace) 622: end. 623: 624: do_init_per_group(C, ConfigIn) -> 625: Config0 = create_users(ConfigIn), 626: case C of 627: riak_timed_yz_buckets -> 628: [{archive_wait, 2500} | Config0]; 629: cassandra -> 630: [{archive_wait, 1500} | Config0]; 631: elasticsearch -> 632: [{archive_wait, 2500} | Config0]; 633: _ -> 634: Config0 635: end. 636: 637: end_per_group(G, Config) when G == rsm_all; G == nostore; 638: G == mam04; G == rsm04; G == with_rsm04; G == muc04; G == muc_rsm04; G == rsm04_comp; 639: G == muc06; G == mam06; 640: G == archived; G == mam_metrics -> 641: Config; 642: end_per_group(muc_configurable_archiveid, Config) -> 643: dynamic_modules:restore_modules(Config), 644: Config; 645: end_per_group(configurable_archiveid, Config) -> 646: dynamic_modules:restore_modules(Config), 647: Config; 648: end_per_group(muc_rsm_all, Config) -> 649: destroy_room(Config); 650: end_per_group(Group, Config) -> 651: C = configuration(Group), 652: B = basic_group(Group), 653: Config0 = dynamic_modules:restore_modules(Config), 654: Config1 = end_state(C, B, Config0), 655: Config2 = end_modules(C, B, Config1), 656: escalus_fresh:clean(), 657: delete_users(Config2). 658: 659: init_modules(rdbms, muc_light, Config) -> 660: Config1 = init_modules_for_muc_light(rdbms, Config), 661: init_module(host_type(), mod_mam_rdbms_user, [muc, pm]), 662: init_module(host_type(), mod_mam_muc_rdbms_arch, []), 663: Config1; 664: init_modules(BT = riak_timed_yz_buckets, muc_light, Config) -> 665: dynamic_modules:start(host_type(), mod_muc_light, [{host, subhost_pattern(muc_light_helper:muc_host_pattern())}]), 666: init_modules(BT, generic, [{muc_domain, muc_light_helper:muc_host_pattern()} | Config]); 667: init_modules(BT = cassandra, muc_light, Config) -> 668: init_modules_for_muc_light(BT, Config); 669: init_modules(cassandra, muc_all, Config) -> 670: init_module(host_type(), mod_mam_muc_cassandra_arch, []), 671: init_module(host_type(), mod_mam_muc, [{host, subhost_pattern(muc_domain(Config))}]), 672: Config; 673: init_modules(BT = elasticsearch, muc_light, Config) -> 674: init_modules_for_muc_light(BT, Config); 675: init_modules(elasticsearch, muc_all, Config) -> 676: init_module(host_type(), mod_mam_muc_elasticsearch_arch, []), 677: init_module(host_type(), mod_mam_muc, [{host, subhost_pattern(muc_domain(Config))}]), 678: Config; 679: init_modules(rdbms, C, Config) when C =:= muc_all; 680: C =:= muc_disabled_retraction -> 681: init_module(host_type(), mod_mam_muc_rdbms_arch, []), 682: init_module(host_type(), mod_mam_rdbms_prefs, [muc]), 683: init_module(host_type(), mod_mam_rdbms_user, [muc, pm]), 684: init_module(host_type(), mod_mam_muc, [{host, subhost_pattern(muc_domain(Config))}] ++ 685: addin_mam_options(C, Config)), 686: Config; 687: init_modules(rdbms_simple, C, Config) when C =:= muc_all; 688: C =:= muc_disabled_retraction -> 689: init_module(host_type(), mod_mam_muc_rdbms_arch, []), 690: init_module(host_type(), mod_mam_rdbms_prefs, [muc]), 691: init_module(host_type(), mod_mam_rdbms_user, [muc, pm]), 692: init_module(host_type(), mod_mam_muc, [{host, subhost_pattern(muc_domain(Config))}] ++ 693: addin_mam_options(C, Config)), 694: Config; 695: init_modules(rdbms_async_pool, C, Config) when C =:= muc_all; 696: C =:= muc_disabled_retraction -> 697: init_module(host_type(), mod_mam_muc_rdbms_arch, [no_writer]), 698: init_module(host_type(), mod_mam_rdbms_arch_async, [{muc, [{flush_interval, 1}]}]), %% 1ms 699: init_module(host_type(), mod_mam_rdbms_prefs, [muc]), 700: init_module(host_type(), mod_mam_rdbms_user, [muc, pm]), 701: init_module(host_type(), mod_mam_muc, [{host, subhost_pattern(muc_domain(Config))}] ++ 702: addin_mam_options(C, Config)), 703: set_wait_for_parallel_writer(muc, Config); 704: init_modules(rdbms_mnesia, C, Config) when C =:= muc_all; 705: C =:= muc_disabled_retraction -> 706: init_module(host_type(), mod_mam_muc_rdbms_arch, []), 707: init_module(host_type(), mod_mam_mnesia_prefs, [muc]), 708: init_module(host_type(), mod_mam_rdbms_user, [muc, pm]), 709: init_module(host_type(), mod_mam_muc, [{host, subhost_pattern(muc_domain(Config))}] ++ 710: addin_mam_options(C, Config)), 711: Config; 712: init_modules(rdbms_cache, C, Config) when C =:= muc_all; 713: C =:= muc_disabled_retraction -> 714: init_module(host_type(), mod_mam_muc_rdbms_arch, []), 715: init_module(host_type(), mod_mam_rdbms_prefs, [muc]), 716: init_module(host_type(), mod_mam_rdbms_user, [muc, pm]), 717: init_module(host_type(), mod_mam_cache_user, [muc]), 718: init_module(host_type(), mod_mam_muc, [{host, subhost_pattern(muc_domain(Config))}] ++ 719: addin_mam_options(C, Config)), 720: Config; 721: init_modules(rdbms_async_cache, C, Config) when C =:= muc_all; 722: C =:= muc_disabled_retraction -> 723: init_module(host_type(), mod_mam_muc_rdbms_arch, [no_writer]), 724: init_module(host_type(), mod_mam_rdbms_arch_async, [{muc, [{flush_interval, 1}]}]), %% 1ms 725: init_module(host_type(), mod_mam_rdbms_prefs, [muc]), 726: init_module(host_type(), mod_mam_rdbms_user, [muc, pm]), 727: init_module(host_type(), mod_mam_cache_user, [muc]), 728: init_module(host_type(), mod_mam_muc, [{host, subhost_pattern(muc_domain(Config))}] ++ 729: addin_mam_options(C, Config)), 730: set_wait_for_parallel_writer(muc, Config); 731: init_modules(rdbms_mnesia_muc_cache, C, Config) when C =:= muc_all; 732: C =:= muc_disabled_retraction -> 733: init_module(host_type(), mod_mam_muc_rdbms_arch, []), 734: init_module(host_type(), mod_mam_mnesia_prefs, [muc]), 735: init_module(host_type(), mod_mam_rdbms_user, [muc, pm]), 736: init_module(host_type(), mod_mam_cache_user, [muc]), 737: init_module(host_type(), mod_mam_muc, [{host, subhost_pattern(muc_domain(Config))}] ++ 738: addin_mam_options(C, Config)), 739: Config; 740: init_modules(rdbms_mnesia_muc_cache, _, _Config) -> 741: skip; 742: init_modules(rdbms_mnesia_cache, C, Config) when C =:= muc_all; 743: C =:= muc_disabled_retraction -> 744: init_module(host_type(), mod_mam_muc_rdbms_arch, []), 745: init_module(host_type(), mod_mam_mnesia_prefs, [muc]), 746: init_module(host_type(), mod_mam_rdbms_user, [muc, pm]), 747: init_module(host_type(), mod_mam_cache_user, [muc]), 748: init_module(host_type(), mod_mam_muc, [{host, subhost_pattern(muc_domain(Config))}] ++ 749: addin_mam_options(C, Config)), 750: Config; 751: init_modules(BackendType, muc_light, Config) -> 752: Config1 = init_modules_for_muc_light(BackendType, Config), 753: case BackendType of 754: cassandra -> ok; 755: elasticsearch -> ok; 756: _ -> 757: init_module(host_type(), mod_mam_rdbms_arch_async, [{muc, [{flush_interval, 1}]}, 758: {pm, [{flush_interval, 1}]}]), 759: init_module(host_type(), mod_mam_rdbms_user, [muc, pm]) 760: end, 761: Config1; 762: init_modules(rdbms, C, Config) -> 763: init_module(host_type(), mod_mam, addin_mam_options(C, Config)), 764: init_module(host_type(), mod_mam_rdbms_arch, []), 765: init_module(host_type(), mod_mam_rdbms_prefs, [pm]), 766: init_module(host_type(), mod_mam_rdbms_user, [pm]), 767: Config; 768: init_modules(rdbms_simple, C, Config) -> 769: init_module(host_type(), mod_mam, addin_mam_options(C, Config)), 770: init_module(host_type(), mod_mam_rdbms_arch, [rdbms_simple_opts()]), 771: init_module(host_type(), mod_mam_rdbms_prefs, [pm]), 772: init_module(host_type(), mod_mam_rdbms_user, [pm]), 773: Config; 774: init_modules(riak_timed_yz_buckets, C, Config) -> 775: init_module(host_type(), mod_mam_riak_timed_arch_yz, [pm, muc]), 776: init_module(host_type(), mod_mam_mnesia_prefs, [pm, muc, 777: {archive_key, mam_archive_key_server_user}]), 778: init_module(host_type(), mod_mam, addin_mam_options(C, Config)), 779: init_module(host_type(), mod_mam_muc, [{host, subhost_pattern(muc_domain(Config))}] ++ 780: addin_mam_options(C, Config)), 781: Config; 782: init_modules(cassandra, C, Config) -> 783: init_module(host_type(), mod_mam_cassandra_arch, [pm]), 784: init_module(host_type(), mod_mam_cassandra_prefs, [pm]), 785: init_module(host_type(), mod_mam, addin_mam_options(C, Config)), 786: Config; 787: init_modules(elasticsearch, C, Config) -> 788: init_module(host_type(), mod_mam_elasticsearch_arch, [pm]), 789: init_module(host_type(), mod_mam_mnesia_prefs, [pm]), 790: init_module(host_type(), mod_mam, addin_mam_options(C, Config)), 791: Config; 792: init_modules(rdbms_async_pool, C, Config) -> 793: init_module(host_type(), mod_mam, addin_mam_options(C, Config)), 794: init_module(host_type(), mod_mam_rdbms_arch, [no_writer]), 795: init_module(host_type(), mod_mam_rdbms_arch_async, [{pm, [{flush_interval, 1}]}]), %% 1ms 796: init_module(host_type(), mod_mam_rdbms_prefs, [pm]), 797: init_module(host_type(), mod_mam_rdbms_user, [pm]), 798: set_wait_for_parallel_writer(pm, Config); 799: init_modules(rdbms_mnesia, C, Config) -> 800: init_module(host_type(), mod_mam, addin_mam_options(C, Config)), 801: init_module(host_type(), mod_mam_rdbms_arch, []), 802: init_module(host_type(), mod_mam_mnesia_prefs, [pm]), 803: init_module(host_type(), mod_mam_rdbms_user, [pm]), 804: Config; 805: init_modules(rdbms_cache, C, Config) -> 806: init_module(host_type(), mod_mam, addin_mam_options(C, Config)), 807: init_module(host_type(), mod_mam_rdbms_arch, []), 808: init_module(host_type(), mod_mam_rdbms_prefs, [pm]), 809: init_module(host_type(), mod_mam_rdbms_user, [pm]), 810: init_module(host_type(), mod_mam_cache_user, [pm]), 811: Config; 812: init_modules(rdbms_async_cache, C, Config) -> 813: init_module(host_type(), mod_mam, addin_mam_options(C, Config)), 814: init_module(host_type(), mod_mam_rdbms_arch, [no_writer]), 815: init_module(host_type(), mod_mam_rdbms_arch_async, [{pm, [{flush_interval, 1}]}]), %% 1ms 816: init_module(host_type(), mod_mam_rdbms_prefs, [pm]), 817: init_module(host_type(), mod_mam_rdbms_user, [pm]), 818: init_module(host_type(), mod_mam_cache_user, [pm]), 819: set_wait_for_parallel_writer(pm, Config); 820: init_modules(rdbms_mnesia_cache, C, Config) -> 821: init_module(host_type(), mod_mam, addin_mam_options(C, Config)), 822: init_module(host_type(), mod_mam_rdbms_arch, []), 823: init_module(host_type(), mod_mam_mnesia_prefs, [pm]), 824: init_module(host_type(), mod_mam_rdbms_user, [pm]), 825: init_module(host_type(), mod_mam_cache_user, [pm]), 826: Config. 827: 828: rdbms_simple_opts() -> 829: [{db_jid_format, mam_jid_rfc}, {db_message_format, mam_message_xml}]. 830: 831: init_modules_for_muc_light(BackendType, Config) -> 832: dynamic_modules:restart(host_type(), mod_muc_light, [{host, subhost_pattern(muc_light_helper:muc_host_pattern())}]), 833: Config1 = init_modules(BackendType, muc_all, [{muc_domain, muc_light_helper:muc_host_pattern()} | Config]), 834: init_modules(BackendType, pm, [{archive_groupchats, false} | Config1]). 835: 836: end_modules(C, muc_light, Config) -> 837: end_modules(C, generic, Config), 838: dynamic_modules:stop(host_type(), mod_muc_light), 839: Config; 840: end_modules(_, _, Config) -> 841: dynamic_modules:ensure_stopped(host_type(), mam_modules()), 842: Config. 843: 844: muc_domain(Config) -> 845: proplists:get_value(muc_domain, Config, muc_helper:muc_host_pattern()). 846: 847: addin_mam_options(disabled_text_search, Config) -> 848: [{full_text_search, false} | addin_mam_options(Config)]; 849: addin_mam_options(C, Config) when C =:= disabled_retraction; 850: C =:= muc_disabled_retraction -> 851: [{message_retraction, false} | addin_mam_options(Config)]; 852: addin_mam_options(chat_markers, Config) -> 853: [{archive_chat_markers, true} | addin_mam_options(Config)]; 854: addin_mam_options(_BasicGroup, Config) -> 855: addin_mam_options(Config). 856: 857: addin_mam_options(Config) -> 858: [{archive_groupchats, proplists:get_value(archive_groupchats, Config, false)}]. 859: 860: mam_modules() -> 861: [mod_mam, 862: mod_mam_muc, 863: mod_mam_cassandra_arch, 864: mod_mam_muc_cassandra_arch, 865: mod_mam_cassandra_prefs, 866: mod_mam_elasticsearch_arch, 867: mod_mam_muc_elasticsearch_arch, 868: mod_mam_rdbms_arch, 869: mod_mam_muc_rdbms_arch, 870: mod_mam_rdbms_arch_async, 871: mod_mam_rdbms_prefs, 872: mod_mam_mnesia_prefs, 873: mod_mam_rdbms_user, 874: mod_mam_cache_user, 875: mod_mam_riak_timed_arch_yz]. 876: 877: init_state(_, muc_all, Config) -> 878: Config; 879: init_state(C, muc_light, Config) -> 880: clean_archives(Config), 881: init_state(C, muc04, Config); 882: init_state(_C, prefs_cases, Config) -> 883: Config; 884: init_state(_, _, Config) -> 885: clean_archives(Config). 886: 887: end_state(C, muc_light, Config) -> 888: muc_light_helper:clear_db(host_type()), 889: end_state(C, generic, Config); 890: end_state(_, _, Config) -> 891: Config. 892: 893: init_per_testcase(C=metrics_incremented_for_async_pools, Config) -> 894: case ?config(configuration, Config) of 895: rdbms_async_pool -> 896: escalus:init_per_testcase(C, clean_archives(Config)); 897: _ -> 898: {skip, "Not an async-pool test"} 899: end; 900: init_per_testcase(C=metric_incremented_when_store_message, ConfigIn) -> 901: Config = case ?config(configuration, ConfigIn) of 902: rdbms_async_pool -> 903: MongooseMetrics = [ 904: {[global, data, rdbms, default], 905: [{recv_oct, '>'}, {send_oct, '>'}]} 906: ], 907: [{mongoose_metrics, MongooseMetrics} | ConfigIn]; 908: _ -> 909: ConfigIn 910: end, 911: escalus:init_per_testcase(C, clean_archives(Config)); 912: init_per_testcase(C=filter_forwarded, Config) -> 913: escalus:init_per_testcase(C, Config); 914: init_per_testcase(C=querying_for_all_messages_with_jid, Config) -> 915: Config1 = escalus_fresh:create_users(Config, [{alice, 1}, {bob, 1}, {carol, 1}]), 916: escalus:init_per_testcase(C, bootstrap_archive(Config1)); 917: init_per_testcase(C=archived, Config) -> 918: Config1 = escalus_fresh:create_users(Config, [{alice, 1}, {bob, 1}]), 919: escalus:init_per_testcase(C, Config1); 920: init_per_testcase(C, Config) when C =:= retract_message; 921: C =:= retract_wrong_message -> 922: skip_if_retraction_not_supported(Config, fun() -> escalus:init_per_testcase(C, Config) end); 923: init_per_testcase(C=retract_message_on_stanza_id, Config) -> 924: Init = fun() -> 925: dynamic_modules:ensure_modules(host_type(), required_modules(C)), 926: escalus:init_per_testcase(C, Config) 927: end, 928: skip_if_retraction_not_supported(Config, Init); 929: init_per_testcase(C=offline_message, Config) -> 930: Config1 = escalus_fresh:create_users(Config, [{alice, 1}, {bob, 1}, {carol, 1}]), 931: escalus:init_per_testcase(C, Config1); 932: init_per_testcase(C=nostore_hint, Config) -> 933: escalus:init_per_testcase(C, Config); 934: init_per_testcase(C=muc_querying_for_all_messages, Config) -> 935: Config1 = escalus_fresh:create_users(Config, [{alice, 1}, {bob, 1}]), 936: escalus:init_per_testcase(C, 937: muc_bootstrap_archive(start_alice_room(Config1))); 938: init_per_testcase(C=muc_querying_for_all_messages_with_jid, Config) -> 939: Config1 = escalus_fresh:create_users(Config, [{alice, 1}, {bob, 1}]), 940: escalus:init_per_testcase(C, 941: muc_bootstrap_archive(start_alice_room(Config1))); 942: init_per_testcase(C=muc_archive_request, Config) -> 943: Config1 = escalus_fresh:create_users(Config, [{alice, 1}, {bob, 1}]), 944: Config2 = %% Check that metric is incremented on MUC flushed 945: case ?config(configuration, Config1) of 946: rdbms_async_pool -> 947: MongooseMetrics = [{['_', 'modMucMamFlushed'], changed}], 948: [{mongoose_metrics, MongooseMetrics} | Config1]; 949: _ -> 950: Config1 951: end, 952: escalus:init_per_testcase(C, start_alice_room(Config2)); 953: init_per_testcase(C=muc_no_elements, Config) -> 954: dynamic_modules:ensure_modules(host_type(), required_modules(C)), 955: Config1 = escalus_fresh:create_users(Config, [{alice, 1}, {bob, 1}]), 956: escalus:init_per_testcase(C, start_alice_room(Config1)); 957: init_per_testcase(C=muc_only_stanzaid, Config) -> 958: dynamic_modules:ensure_modules(host_type(), required_modules(C)), 959: Config1 = escalus_fresh:create_users(Config, [{alice, 1}, {bob, 1}]), 960: escalus:init_per_testcase(C, start_alice_room(Config1)); 961: init_per_testcase(C=no_elements, Config) -> 962: dynamic_modules:ensure_modules(host_type(), required_modules(C)), 963: Config1 = escalus_fresh:create_users(Config, [{alice, 1}, {bob, 1}]), 964: escalus:init_per_testcase(C, start_alice_room(Config1)); 965: init_per_testcase(C=only_stanzaid, Config) -> 966: dynamic_modules:ensure_modules(host_type(), required_modules(C)), 967: Config1 = escalus_fresh:create_users(Config, [{alice, 1}, {bob, 1}]), 968: escalus:init_per_testcase(C, start_alice_room(Config1)); 969: init_per_testcase(C=same_stanza_id, Config) -> 970: dynamic_modules:ensure_modules(host_type(), required_modules(C)), 971: Config1 = escalus_fresh:create_users(Config, [{alice, 1}, {bob, 1}]), 972: escalus:init_per_testcase(C, start_alice_room(Config1)); 973: init_per_testcase(C=muc_message_with_stanzaid, Config) -> 974: Config1 = escalus_fresh:create_users(Config, [{alice, 1}, {bob, 1}]), 975: escalus:init_per_testcase(C, start_alice_room(Config1)); 976: init_per_testcase(C, Config) when C =:= retract_muc_message; 977: C =:= retract_muc_message_on_stanza_id; 978: C =:= retract_wrong_muc_message -> 979: Init = fun() -> 980: Config1 = escalus_fresh:create_users(Config, [{alice, 1}, {bob, 1}]), 981: escalus:init_per_testcase(C, start_alice_room(Config1)) 982: end, 983: skip_if_retraction_not_supported(Config, Init); 984: init_per_testcase(C=muc_multiple_devices, Config) -> 985: Config1 = escalus_fresh:create_users(Config, [{alice, 1}, {bob, 1}]), 986: escalus:init_per_testcase(C, start_alice_room(Config1)); 987: init_per_testcase(C=muc_protected_message, Config) -> 988: Config1 = escalus_fresh:create_users(Config, [{alice, 1}, {bob, 1}]), 989: escalus:init_per_testcase(C, start_alice_room(Config1)); 990: init_per_testcase(C=muc_deny_protected_room_access, Config) -> 991: Config1 = escalus_fresh:create_users(Config, [{alice, 1}, {bob, 1}]), 992: escalus:init_per_testcase(C, start_alice_protected_room(Config1)); 993: init_per_testcase(C=muc_allow_access_to_owner, Config) -> 994: Config1 = escalus_fresh:create_users(Config, [{alice, 1}, {bob, 1}]), 995: escalus:init_per_testcase(C, start_alice_protected_room(Config1)); 996: init_per_testcase(C=muc_delete_x_user_in_anon_rooms, Config) -> 997: Config1 = escalus_fresh:create_users(Config, [{alice, 1}, {bob, 1}]), 998: escalus:init_per_testcase(C, start_alice_anonymous_room(Config1)); 999: init_per_testcase(C=muc_show_x_user_to_moderators_in_anon_rooms, Config) -> 1000: Config1 = escalus_fresh:create_users(Config, [{alice, 1}, {bob, 1}]), 1001: escalus:init_per_testcase(C, start_alice_anonymous_room(Config1)); 1002: init_per_testcase(C=muc_show_x_user_for_your_own_messages_in_anon_rooms, Config) -> 1003: Config1 = escalus_fresh:create_users(Config, [{alice, 1}, {bob, 1}]), 1004: escalus:init_per_testcase(C, start_alice_anonymous_room(Config1)); 1005: init_per_testcase(C=range_archive_request_not_empty, Config) -> 1006: Config1 = escalus_fresh:create_users(Config, [{alice, 1}, {bob, 1}, {carol, 1}]), 1007: escalus:init_per_testcase(C, bootstrap_archive(Config1)); 1008: init_per_testcase(C=limit_archive_request, Config) -> 1009: Config1 = escalus_fresh:create_users(Config, [{alice, 1}, {bob, 1}, {carol, 1}]), 1010: escalus:init_per_testcase(C, bootstrap_archive(Config1)); 1011: init_per_testcase(C=prefs_set_request, Config) -> 1012: skip_if_riak(C, Config); 1013: init_per_testcase(C=prefs_set_cdata_request, Config) -> 1014: skip_if_riak(C, Config); 1015: init_per_testcase(C=simple_text_search_request, Config) -> 1016: skip_if_cassandra(Config, fun() -> escalus:init_per_testcase(C, Config) end); 1017: init_per_testcase(C=long_text_search_request, Config) -> 1018: skip_if_cassandra(Config, fun() -> escalus:init_per_testcase(C, Config) end); 1019: init_per_testcase(C=save_unicode_messages, Config) -> 1020: skip_if_cassandra(Config, fun() -> escalus:init_per_testcase(C, Config) end); 1021: init_per_testcase(C=muc_text_search_request, Config) -> 1022: Init = 1023: fun() -> 1024: Config1 = escalus_fresh:create_users(Config, [{alice, 1}, {bob, 1}]), 1025: escalus:init_per_testcase(C, start_alice_room(Config1)) 1026: end, 1027: 1028: skip_if_cassandra(Config, Init); 1029: init_per_testcase(C = muc_light_stored_in_pm_if_allowed_to, Config) -> 1030: dynamic_modules:ensure_modules(host_type(), required_modules(C, Config)), 1031: clean_archives(Config), 1032: escalus:init_per_testcase(C, Config); 1033: init_per_testcase(C, Config) when C =:= muc_light_simple; 1034: C =:= muc_light_shouldnt_modify_pm_archive; 1035: C =:= muc_light_chat_markers_are_archived_if_enabled; 1036: C =:= muc_light_chat_markers_are_not_archived_if_disabled-> 1037: dynamic_modules:ensure_modules(host_type(), required_modules(C, Config)), 1038: escalus:init_per_testcase(C, Config); 1039: init_per_testcase(C=archive_chat_markers, Config) -> 1040: Config1 = escalus_fresh:create_users(Config, [{alice, 1}, {bob, 1}]), 1041: escalus:init_per_testcase(C, Config1); 1042: init_per_testcase(C=dont_archive_chat_markers, Config) -> 1043: Config1 = escalus_fresh:create_users(Config, [{alice, 1}, {bob, 1}]), 1044: escalus:init_per_testcase(C, Config1); 1045: init_per_testcase(CaseName, Config) -> 1046: escalus:init_per_testcase(CaseName, Config). 1047: 1048: skip_if_retraction_not_supported(Config, Init) -> 1049: case lists:member(?config(configuration, Config), rdbms_configs(true)) of 1050: false -> 1051: {skip, "message retraction not supported"}; 1052: true -> 1053: Init() 1054: end. 1055: 1056: skip_if_riak(C, Config) -> 1057: case ?config(configuration, Config) of 1058: riak_timed_yz_buckets -> 1059: {skip, "prefs not implemented for riak"}; 1060: _ -> 1061: escalus:init_per_testcase(C, Config) 1062: end. 1063: 1064: skip_if_cassandra(Config, Init) -> 1065: case ?config(configuration, Config) of 1066: cassandra -> 1067: {skip, "full text search is not implemented for cassandra backend"}; 1068: _ -> 1069: Init() 1070: end. 1071: 1072: end_per_testcase(C=muc_text_search_request, Config) -> 1073: destroy_room(Config), 1074: escalus:end_per_testcase(C, Config); 1075: end_per_testcase(C=muc_archive_request, Config) -> 1076: destroy_room(Config), 1077: escalus:end_per_testcase(C, Config); 1078: end_per_testcase(C=muc_multiple_devices, Config) -> 1079: destroy_room(Config), 1080: escalus:end_per_testcase(C, Config); 1081: end_per_testcase(C=muc_protected_message, Config) -> 1082: destroy_room(Config), 1083: escalus:end_per_testcase(C, Config); 1084: end_per_testcase(C=muc_deny_protected_room_access, Config) -> 1085: destroy_room(Config), 1086: escalus:end_per_testcase(C, Config); 1087: end_per_testcase(C=muc_allow_access_to_owner, Config) -> 1088: destroy_room(Config), 1089: escalus:end_per_testcase(C, Config); 1090: end_per_testcase(C=muc_delete_x_user_in_anon_rooms, Config) -> 1091: destroy_room(Config), 1092: escalus:end_per_testcase(C, Config); 1093: end_per_testcase(C=muc_show_x_user_to_moderators_in_anon_rooms, Config) -> 1094: destroy_room(Config), 1095: escalus:end_per_testcase(C, Config); 1096: end_per_testcase(C=muc_show_x_user_for_your_own_messages_in_anon_rooms, Config) -> 1097: destroy_room(Config), 1098: escalus:end_per_testcase(C, Config); 1099: end_per_testcase(C=muc_querying_for_all_messages, Config) -> 1100: destroy_room(Config), 1101: escalus:end_per_testcase(C, Config); 1102: end_per_testcase(C=muc_querying_for_all_messages_with_jid, Config) -> 1103: destroy_room(Config), 1104: escalus:end_per_testcase(C, Config); 1105: end_per_testcase(C=muc_message_with_stanzaid, Config) -> 1106: destroy_room(Config), 1107: escalus:end_per_testcase(C, Config); 1108: end_per_testcase(C, Config) when C =:= retract_muc_message; 1109: C =:= retract_muc_message_on_stanza_id; 1110: C =:= retract_wrong_muc_message -> 1111: destroy_room(Config), 1112: escalus:end_per_testcase(C, Config); 1113: end_per_testcase(C=muc_no_elements, Config) -> 1114: destroy_room(Config), 1115: escalus:end_per_testcase(C, Config); 1116: end_per_testcase(C=muc_only_stanzaid, Config) -> 1117: destroy_room(Config), 1118: escalus:end_per_testcase(C, Config); 1119: end_per_testcase(C = muc_light_stored_in_pm_if_allowed_to, Config) -> 1120: escalus:end_per_testcase(C, Config); 1121: end_per_testcase(C = retract_message_on_stanza_id, Config) -> 1122: escalus:end_per_testcase(C, Config); 1123: end_per_testcase(C = muc_light_chat_markers_are_archived_if_enabled, Config) -> 1124: escalus:end_per_testcase(C, Config); 1125: end_per_testcase(C = muc_light_chat_markers_are_not_archived_if_disabled, Config) -> 1126: escalus:end_per_testcase(C, Config); 1127: end_per_testcase(C = no_elements, Config) -> 1128: escalus:end_per_testcase(C, Config); 1129: end_per_testcase(C = only_stanzaid, Config) -> 1130: escalus:end_per_testcase(C, Config); 1131: end_per_testcase(C = same_stanza_id, Config) -> 1132: escalus:end_per_testcase(C, Config); 1133: end_per_testcase(CaseName, Config) -> 1134: escalus:end_per_testcase(CaseName, Config). 1135: 1136: %% Module configuration per testcase 1137: 1138: required_modules(Case) -> 1139: required_modules(Case, []). 1140: 1141: % muc_light basic group 1142: required_modules(muc_light_simple, Config) -> 1143: [{mod_mam, [{archive_groupchats, false}]}, 1144: {mod_mam_muc, dynamic_modules:get_saved_config(host_type(), mod_mam_muc, Config)}]; 1145: required_modules(muc_light_shouldnt_modify_pm_archive, Config) -> 1146: [{mod_mam, [{archive_groupchats, false}]}, 1147: {mod_mam_muc, dynamic_modules:get_saved_config(host_type(), mod_mam_muc, Config)}]; 1148: required_modules(muc_light_stored_in_pm_if_allowed_to, Config) -> 1149: [{mod_mam, [{archive_groupchats, true}]}, 1150: {mod_mam_muc, dynamic_modules:get_saved_config(host_type(), mod_mam_muc, Config)}]; 1151: required_modules(muc_light_chat_markers_are_archived_if_enabled, Config) -> 1152: Opts = dynamic_modules:get_saved_config(host_type(), mod_mam_muc, Config), 1153: [{mod_mam, [{archive_groupchats, false}]}, 1154: {mod_mam_muc, [{archive_chat_markers, true} | Opts]}]; 1155: required_modules(muc_light_chat_markers_are_not_archived_if_disabled, Config) -> 1156: Opts = dynamic_modules:get_saved_config(host_type(), mod_mam_muc, Config), 1157: [{mod_mam, [{archive_groupchats, false}]}, 1158: {mod_mam_muc, [{archive_chat_markers, false} | Opts]}]; 1159: % muc_configurable_archiveid basic group 1160: required_modules(muc_no_elements, _Config) -> 1161: [{mod_mam_muc, [no_stanzaid_element]}]; 1162: required_modules(muc_only_stanzaid, _Config) -> 1163: [{mod_mam_muc, []}]; 1164: % configurable_archiveid basic group 1165: required_modules(no_elements, _Config) -> 1166: [{mod_mam, [no_stanzaid_element]}]; 1167: required_modules(only_stanzaid, _Config) -> 1168: [{mod_mam, []}]; 1169: required_modules(same_stanza_id, _Config) -> 1170: [{mod_mam, [same_mam_id_for_peers]}]; 1171: required_modules(retract_message_on_stanza_id, _Config) -> 1172: [{mod_mam, [{same_mam_id_for_peers, true}]}]; 1173: required_modules(_, _) -> 1174: []. 1175: 1176: init_module(Host, Mod, Args) -> 1177: lists:member(Mod, mam_modules()) orelse ct:fail("Unknown module ~p", [Mod]), 1178: dynamic_modules:ensure_modules(Host, [{Mod, Args}]). 1179: 1180: %%-------------------------------------------------------------------- 1181: %% Group name helpers 1182: %%-------------------------------------------------------------------- 1183: 1184: full_group(Conf, Group) -> 1185: list_to_atom(atom_to_list(Conf) ++ "_" ++ atom_to_list(Group)). 1186: 1187: %% @doc Delete suffix. 1188: configuration(Group) -> 1189: match_atom_prefix(Group, make_greedy(configurations())). 1190: 1191: %% @doc Rearrange a list of strings (or atoms), that all prefixes 1192: %% will be tested. 1193: %% 1194: %% Example: 1195: %% `make_greedy(rdbms_mnesia_muc, [rdbms, rdbms_mnesia]) -> rdbms' 1196: %% `make_greedy(rdbms_mnesia_muc, match_longer_first([rdbms, rdbms_mnesia])) -> rdbms_mnesia' 1197: %% @end 1198: make_greedy(List) -> 1199: lists:reverse(lists:usort(List)). 1200: 1201: %% @doc Delete prefix. 1202: basic_group(Group) -> 1203: basic_group(Group, configuration(Group)). 1204: 1205: basic_group(Group, Conf) -> 1206: ConfS = atom_to_list(Conf), 1207: GroupS = atom_to_list(Group), 1208: list_to_atom(delete_delimiter(delete_prefix(ConfS, GroupS))). 1209: 1210: match_atom_prefix(Target, Prefixes) -> 1211: match_atom_prefix1(atom_to_list(Target), Prefixes). 1212: 1213: match_atom_prefix1(TargetS, [PrefixA | Prefixes]) -> 1214: PrefixS = atom_to_list(PrefixA), 1215: case lists:prefix(PrefixS, TargetS) of 1216: true -> PrefixA; 1217: false -> match_atom_prefix1(TargetS, Prefixes) 1218: end. 1219: 1220: delete_prefix([H|Prefix], [H|Target]) -> 1221: delete_prefix(Prefix, Target); 1222: delete_prefix([], Target) -> 1223: Target. 1224: 1225: delete_delimiter("_" ++ Tail) -> 1226: Tail. 1227: 1228: %%-------------------------------------------------------------------- 1229: %% Adhoc tests 1230: %%-------------------------------------------------------------------- 1231: 1232: % @doc Helper function, sends an example message to a user and checks 1233: % if archive id elements are defined or not 1234: send_and_check_archive_elements(Config, Archived, Stanzaid) -> 1235: F = fun(Alice, Bob) -> 1236: %% Archive must be empty. 1237: %% Alice sends "OH, HAI!" to Bob. 1238: escalus:send(Alice, escalus_stanza:chat_to(Bob, <<"OH, HAI!">>)), 1239: 1240: %% Bob receives a message. 1241: BobMsg = escalus:wait_for_stanza(Bob), 1242: case exml_query:subelement(BobMsg, <<"archived">>) of 1243: undefined -> 1244: ?assert_equal(Archived, false); 1245: _ -> 1246: ?assert_equal(Archived, true) 1247: end, 1248: case exml_query:subelement(BobMsg, <<"stanza-id">>) of 1249: undefined -> 1250: ?assert_equal(Stanzaid, false); 1251: _ -> 1252: ?assert_equal(Stanzaid, true) 1253: end, 1254: ok 1255: end, 1256: %% Made fresh in init_per_testcase 1257: escalus:story(Config, [{alice, 1}, {bob, 1}], F). 1258: 1259: % @doc Helper function, sends an example message to a room and checks 1260: % if archive id elements are defined or not 1261: muc_send_and_check_archive_elements(Config, Archived, Stanzaid) -> 1262: F = fun(Alice, Bob) -> 1263: Room = ?config(room, Config), 1264: RoomAddr = room_address(Room), 1265: Text = <<"Hi, Bob!">>, 1266: escalus:send(Alice, stanza_muc_enter_room(Room, nick(Alice))), 1267: escalus:send(Bob, stanza_muc_enter_room(Room, nick(Bob))), 1268: 1269: %% Bob received presences. 1270: escalus:wait_for_stanzas(Bob, 2), 1271: 1272: %% Bob received the room's subject. 1273: escalus:wait_for_stanzas(Bob, 1), 1274: 1275: %% Alice sends another message to Bob. 1276: %% The message is not archived by the room. 1277: escalus:send(Alice, escalus_stanza:chat_to(Bob, <<"OH, HAI!">>)), 1278: escalus:assert(is_message, escalus:wait_for_stanza(Bob)), 1279: 1280: %% Alice sends to the chat room. 1281: escalus:send(Alice, escalus_stanza:groupchat_to(RoomAddr, Text)), 1282: 1283: %% Bob received the message "Hi, Bob!". 1284: %% This message will be archived (by alicesroom@localhost). 1285: %% User's archive is disabled (i.e. bob@localhost). 1286: BobMsg = escalus:wait_for_stanza(Bob), 1287: escalus:assert(is_message, BobMsg), 1288: case exml_query:subelement(BobMsg, <<"archived">>) of 1289: undefined -> 1290: ?assert_equal(Archived, false); 1291: _ -> 1292: ?assert_equal(Archived, true) 1293: end, 1294: case exml_query:subelement(BobMsg, <<"stanza-id">>) of 1295: undefined -> 1296: ?assert_equal(Stanzaid, false); 1297: _ -> 1298: ?assert_equal(Stanzaid, true) 1299: end, 1300: ok 1301: end, 1302: escalus:story(Config, [{alice, 1}, {bob, 1}], F). 1303: 1304: %% Archive id elements should be present when config says so 1305: muc_no_elements(Config) -> 1306: muc_send_and_check_archive_elements(Config, false, false). 1307: 1308: muc_only_stanzaid(Config) -> 1309: muc_send_and_check_archive_elements(Config, false, true). 1310: 1311: no_elements(Config) -> 1312: send_and_check_archive_elements(Config, false, false). 1313: 1314: only_stanzaid(Config) -> 1315: send_and_check_archive_elements(Config, false, true). 1316: 1317: same_stanza_id(Config) -> 1318: P = ?config(props, Config), 1319: F = fun(Alice, Bob) -> 1320: Body = <<"OH, HAI!">>, 1321: Msg = escalus_stanza:chat_to(Bob, Body), 1322: escalus:send(Alice, Msg), 1323: mam_helper:wait_for_archive_size(Alice, 1), 1324: escalus:send(Alice, stanza_archive_request(P, <<"q1">>)), 1325: Result = wait_archive_respond(Alice), 1326: [AliceCopyOfMessage] = respond_messages(Result), 1327: AliceId = exml_query:path(AliceCopyOfMessage, [{element, <<"result">>}, {attr, <<"id">>}]), 1328: %% ... and Bob receives the message 1329: RecvMsg = escalus:wait_for_stanza(Bob), 1330: BobId = exml_query:path(RecvMsg, [{element, <<"stanza-id">>}, {attr, <<"id">>}]), 1331: ?assert_equal(AliceId, BobId) 1332: end, 1333: escalus_fresh:story(Config, [{alice, 1}, {bob, 1}], F). 1334: 1335: %% Querying the archive for messages 1336: simple_archive_request(Config) -> 1337: P = ?config(props, Config), 1338: F = fun(Alice, Bob) -> 1339: %% Alice sends "OH, HAI!" to Bob 1340: %% {xmlel,<<"message">>, 1341: %% [{<<"from">>,<<"alice@localhost/res1">>}, 1342: %% {<<"to">>,<<"bob@localhost/res1">>}, 1343: %% {<<"xml:lang">>,<<"en">>}, 1344: %% {<<"type">>,<<"chat">>}], 1345: %% [{xmlel,<<"body">>,[],[{xmlcdata,<<"OH, HAI!">>}]}]} 1346: escalus:send(Alice, escalus_stanza:chat_to(Bob, <<"OH, HAI!">>)), 1347: mam_helper:wait_for_archive_size(Alice, 1), 1348: escalus:send(Alice, stanza_archive_request(P, <<"q1">>)), 1349: Res = wait_archive_respond(Alice), 1350: assert_respond_size(1, Res), 1351: assert_respond_query_id(P, <<"q1">>, parse_result_iq(Res)), 1352: ok 1353: end, 1354: escalus_fresh:story(Config, [{alice, 1}, {bob, 1}], F). 1355: 1356: simple_archive_request_for_the_receiver(Config) -> 1357: P = ?config(props, Config), 1358: F = fun(Alice, Bob) -> 1359: escalus:send(Alice, escalus_stanza:chat_to(Bob, <<"OH, HAI!">>)), 1360: BobMsg = escalus:wait_for_stanza(Bob), 1361: escalus:assert(is_message, BobMsg), 1362: mam_helper:wait_for_archive_size(Bob, 1), 1363: escalus:send(Bob, stanza_archive_request(P, <<"q1">>)), 1364: Res = wait_archive_respond(Bob), 1365: assert_respond_size(1, Res), 1366: assert_respond_query_id(P, <<"q1">>, parse_result_iq(Res)), 1367: ok 1368: end, 1369: escalus_fresh:story(Config, [{alice, 1}, {bob, 1}], F). 1370: 1371: text_search_is_not_available(Config) -> 1372: P = ?config(props, Config), 1373: F = fun(Alice) -> 1374: Namespace = get_prop(mam_ns, P), 1375: escalus:send(Alice, stanza_retrieve_form_fields(<<"q">>, Namespace)), 1376: Res = escalus:wait_for_stanza(Alice), 1377: escalus:assert(is_iq_with_ns, [Namespace], Res), 1378: QueryEl = exml_query:subelement(Res, <<"query">>), 1379: XEl = exml_query:subelement(QueryEl, <<"x">>), 1380: Fields = exml_query:paths(XEl, [{element, <<"field">>}]), 1381: HasFullTextSearch = lists:any(fun(Item) -> 1382: exml_query:attr(Item, <<"var">>) == <<"full-text-search">> 1383: end, Fields), 1384: 1385: ?assert_equal(false, HasFullTextSearch) 1386: end, 1387: escalus_fresh:story(Config, [{alice, 1}], F). 1388: 1389: text_search_query_fails_if_disabled(Config) -> 1390: P = ?config(props, Config), 1391: F = fun(_Alice, Bob) -> 1392: escalus:send(Bob, stanza_text_search_archive_request(P, <<"q1">>, 1393: <<"this IQ is expected to fail">>)), 1394: Res = escalus:wait_for_stanza(Bob), 1395: escalus:assert(is_iq_error, Res) 1396: end, 1397: escalus_fresh:story(Config, [{alice, 1}, {bob, 1}], F). 1398: 1399: text_search_is_available(Config) -> 1400: P = ?config(props, Config), 1401: F = fun(Alice) -> 1402: Namespace = get_prop(mam_ns, P), 1403: escalus:send(Alice, stanza_retrieve_form_fields(<<"q">>, Namespace)), 1404: Res = escalus:wait_for_stanza(Alice), 1405: escalus:assert(is_iq_with_ns, [Namespace], Res), 1406: QueryEl = exml_query:subelement(Res, <<"query">>), 1407: XEl = exml_query:subelement(QueryEl, <<"x">>), 1408: escalus:assert(has_field_with_type, [<<"full-text-search">>, <<"text-single">>], XEl), 1409: ok 1410: end, 1411: escalus_fresh:story(Config, [{alice, 1}], F). 1412: 1413: simple_text_search_request(Config) -> 1414: P = ?config(props, Config), 1415: F = fun(Alice, Bob) -> 1416: escalus:send(Alice, escalus_stanza:chat_to(Bob, <<"Hi there! My cat's name is John">>)), 1417: escalus:send(Alice, escalus_stanza:chat_to(Bob, <<"Also my bike broke down so I'm unable ", 1418: "to return him home">>)), 1419: escalus:send(Alice, escalus_stanza:chat_to(Bob, <<"Cats are awesome by the way">>)), 1420: mam_helper:wait_for_archive_size(Alice, 3), 1421: maybe_wait_for_archive(Config), %% yz lag 1422: 1423: %% 'Cat' query 1424: escalus:send(Alice, stanza_text_search_archive_request(P, <<"q1">>, <<"cat">>)), 1425: Res1 = wait_archive_respond(Alice), 1426: assert_respond_size(2, Res1), 1427: assert_respond_query_id(P, <<"q1">>, parse_result_iq(Res1)), 1428: [Msg1, Msg2] = respond_messages(Res1), 1429: #forwarded_message{message_body = Body1} = parse_forwarded_message(Msg1), 1430: #forwarded_message{message_body = Body2} = parse_forwarded_message(Msg2), 1431: ?assert_equal(<<"Hi there! My cat's name is John">>, Body1), 1432: ?assert_equal(<<"Cats are awesome by the way">>, Body2), 1433: 1434: %% 'Bike' query 1435: escalus:send(Alice, stanza_text_search_archive_request(P, <<"q2">>, <<"bike">>)), 1436: Res2 = wait_archive_respond(Alice), 1437: assert_respond_size(1, Res2), 1438: assert_respond_query_id(P, <<"q2">>, parse_result_iq(Res2)), 1439: [Msg3] = respond_messages(Res2), 1440: #forwarded_message{message_body = Body3} = parse_forwarded_message(Msg3), 1441: ?assert_equal(<<"Also my bike broke down so I'm unable to return him home">>, Body3), 1442: 1443: ok 1444: end, 1445: escalus_fresh:story(Config, [{alice, 1}, {bob, 1}], F). 1446: 1447: long_text_search_request(Config) -> 1448: P = ?config(props, Config), 1449: F = fun(Alice, Bob) -> 1450: Msgs = text_search_messages(), 1451: 1452: [ escalus:send(Alice, escalus_stanza:chat_to(Bob, Msg)) || Msg <- Msgs ], 1453: 1454: %% Just check that Bob receives the messages. 1455: %% It should help, when the CI server is overloaded. 1456: %% The test should work without this block. 1457: %% But sometimes on the CI server we ending up with not all messages 1458: %% yet archived, which leads to the test failure. 1459: ExpectedLen = length(Msgs), 1460: BobMessages = escalus:wait_for_stanzas(Bob, ExpectedLen, 15000), 1461: ?assert_equal_extra(ExpectedLen, length(BobMessages), 1462: #{bob_messages => BobMessages}), 1463: 1464: mam_helper:wait_for_archive_size(Bob, ExpectedLen), 1465: mam_helper:wait_for_archive_size(Alice, ExpectedLen), 1466: maybe_wait_for_archive(Config), %% yz lag 1467: escalus:send(Alice, stanza_text_search_archive_request(P, <<"q1">>, 1468: <<"Ribs poRk cUlpa">>)), 1469: Res = wait_archive_respond(Alice), 1470: assert_respond_size(3, Res), 1471: assert_respond_query_id(P, <<"q1">>, parse_result_iq(Res)), 1472: 1473: [Msg1, Msg2, Msg3] = respond_messages(Res), 1474: #forwarded_message{message_body = Body1} = parse_forwarded_message(Msg1), 1475: #forwarded_message{message_body = Body2} = parse_forwarded_message(Msg2), 1476: #forwarded_message{message_body = Body3} = parse_forwarded_message(Msg3), 1477: 1478: ?assert_equal(lists:nth(2, Msgs), Body1), 1479: ?assert_equal(lists:nth(8, Msgs), Body2), 1480: ?assert_equal(lists:nth(11, Msgs), Body3), 1481: 1482: ok 1483: end, 1484: escalus_fresh:story(Config, [{alice, 1}, {bob, 1}], F). 1485: 1486: %% Write and read Unicode messages back 1487: unicode_messages_can_be_extracted(Config) -> 1488: P = ?config(props, Config), 1489: F = fun(Alice, Bob) -> 1490: Texts = [<<"Hi! this is an unicode character lol 😂"/utf8>>, 1491: <<"this is another one no 🙅"/utf8>>, 1492: <<"This is the same again lol 😂"/utf8>>], 1493: 1494: [escalus:send(Alice, escalus_stanza:chat_to(Bob, Text)) 1495: || Text <- Texts], 1496: mam_helper:wait_for_archive_size(Alice, length(Texts)), 1497: 1498: %% WHEN Getting all messages 1499: escalus:send(Alice, stanza_archive_request(P, <<"uni-q">>)), 1500: Res = wait_archive_respond(Alice), 1501: assert_respond_size(3, Res), 1502: 1503: assert_respond_query_id(P, <<"uni-q">>, parse_result_iq(Res)), 1504: [Msg1, Msg2, Msg3] = respond_messages(Res), 1505: #forwarded_message{message_body = Body1} = parse_forwarded_message(Msg1), 1506: #forwarded_message{message_body = Body2} = parse_forwarded_message(Msg2), 1507: #forwarded_message{message_body = Body3} = parse_forwarded_message(Msg3), 1508: ?assert_equal(<<"Hi! this is an unicode character lol 😂"/utf8>>, Body1), 1509: ?assert_equal(<<"this is another one no 🙅"/utf8>>, Body2), 1510: ?assert_equal(<<"This is the same again lol 😂"/utf8>>, Body3), 1511: ok 1512: end, 1513: escalus_fresh:story(Config, [{alice, 1}, {bob, 1}], F). 1514: 1515: %% Depends on search feature 1516: %% Consult with unicode_messages_can_be_extracted, 1517: %% which ensures that unicode messages can be processed 1518: save_unicode_messages(Config) -> 1519: P = ?config(props, Config), 1520: F = fun(Alice, Bob) -> 1521: escalus:send(Alice, escalus_stanza:chat_to(Bob, <<"Hi! this is an unicode character lol 😂"/utf8>>)), 1522: escalus:send(Alice, escalus_stanza:chat_to(Bob, <<"this is another one no 🙅"/utf8>>)), 1523: escalus:send(Alice, escalus_stanza:chat_to(Bob, <<"This is the same again lol 😂"/utf8>>)), 1524: mam_helper:wait_for_archive_size(Alice, 3), 1525: 1526: %% Riak YZ is async. 1527: %% It takes time to index docs (usually 1 second). 1528: %% https://docs.riak.com/riak/kv/latest/developing/usage/search/index.html#indexing-values 1529: %% Each stanza_text_search_archive_request should call it regardless of wait_for_archive_size result. 1530: maybe_wait_for_archive(Config), 1531: 1532: %% WHEN Searching for a message with "lol" string 1533: escalus:send(Alice, stanza_text_search_archive_request(P, <<"q1">>, <<"lol"/utf8>>)), 1534: Res1 = wait_archive_respond(Alice), 1535: assert_respond_size(2, Res1), 1536: assert_respond_query_id(P, <<"q1">>, parse_result_iq(Res1)), 1537: [Msg1, Msg2] = respond_messages(Res1), 1538: #forwarded_message{message_body = Body1} = parse_forwarded_message(Msg1), 1539: #forwarded_message{message_body = Body2} = parse_forwarded_message(Msg2), 1540: ?assert_equal(<<"Hi! this is an unicode character lol 😂"/utf8>>, Body1), 1541: ?assert_equal(<<"This is the same again lol 😂"/utf8>>, Body2), 1542: 1543: escalus:send(Alice, stanza_text_search_archive_request(P, <<"q2">>, <<"another"/utf8>>)), 1544: Res2 = wait_archive_respond(Alice), 1545: assert_respond_size(1, Res2), 1546: assert_respond_query_id(P, <<"q2">>, parse_result_iq(Res2)), 1547: [Msg3] = respond_messages(Res2), 1548: #forwarded_message{message_body = Body3} = parse_forwarded_message(Msg3), 1549: ?assert_equal(<<"this is another one no 🙅"/utf8>>, Body3), 1550: 1551: ok 1552: end, 1553: escalus_fresh:story(Config, [{alice, 1}, {bob, 1}], F). 1554: 1555: stanza_id_is_appended_to_carbons(Config) -> 1556: F = fun(Alice1, Alice2, Bob1, Bob2) -> 1557: Msg = <<"OH, HAI!">>, 1558: mongoose_helper:enable_carbons([Alice1, Alice2, Bob1, Bob2]), 1559: escalus:send(Alice1, escalus_stanza:chat_to(Bob1, Msg)), 1560: mam_helper:wait_for_archive_size(Alice1, 1), 1561: escalus_client:wait_for_stanza(Bob1), 1562: Alice2CC = escalus_client:wait_for_stanza(Alice2), 1563: Bob2CC = escalus_client:wait_for_stanza(Bob2), 1564: 1565: SID = fun(Packet, Direction) -> 1566: exml_query:path(Packet, [{element_with_ns, Direction, <<"urn:xmpp:carbons:2">>}, 1567: {element_with_ns, <<"forwarded">>, <<"urn:xmpp:forward:0">>}, 1568: {element_with_ns, <<"message">>, <<"jabber:client">>}, 1569: {element_with_ns, <<"stanza-id">>, <<"urn:xmpp:sid:0">>}, 1570: {attr, <<"id">>}]) 1571: end, 1572: ?assert_equal(true, undefined =/= SID(Bob2CC, <<"received">>)), 1573: ?assert_equal(true, undefined =/= SID(Alice2CC, <<"sent">>)), 1574: escalus:assert(is_forwarded_sent_message, 1575: [escalus_client:full_jid(Alice1), escalus_client:full_jid(Bob1), Msg], Alice2CC), 1576: escalus:assert(is_forwarded_received_message, 1577: [escalus_client:full_jid(Alice1), escalus_client:full_jid(Bob1), Msg], Bob2CC) 1578: end, 1579: escalus_fresh:story(Config, [{alice, 2}, {bob, 2}], F). 1580: 1581: muc_text_search_request(Config) -> 1582: P = ?config(props, Config), 1583: F = fun(Alice, Bob) -> 1584: Room = ?config(room, Config), 1585: escalus:send(Alice, stanza_muc_enter_room(Room, nick(Alice))), 1586: escalus:send(Bob, stanza_muc_enter_room(Room, nick(Bob))), 1587: 1588: %% Bob received presences. 1589: escalus:wait_for_stanzas(Bob, 2), 1590: 1591: %% Bob received the room's subject. 1592: escalus:wait_for_stanzas(Bob, 1), 1593: 1594: Msgs = text_search_messages(), 1595: 1596: lists:foreach( 1597: fun(Msg) -> 1598: Stanza = escalus_stanza:groupchat_to(room_address(Room), Msg), 1599: escalus:send(Alice, Stanza), 1600: escalus:assert(is_message, escalus:wait_for_stanza(Bob)) 1601: end, Msgs), 1602: 1603: maybe_wait_for_archive(Config), 1604: SearchStanza = stanza_text_search_archive_request(P, <<"q1">>, <<"Ribs poRk cUlpa">>), 1605: escalus:send(Bob, stanza_to_room(SearchStanza, Room)), 1606: Res = wait_archive_respond(Bob), 1607: assert_respond_size(3, Res), 1608: assert_respond_query_id(P, <<"q1">>, parse_result_iq(Res)), 1609: 1610: [Msg1, Msg2, Msg3] = respond_messages(Res), 1611: #forwarded_message{message_body = Body1} = parse_forwarded_message(Msg1), 1612: ?assert_equal(lists:nth(2, Msgs), Body1), 1613: #forwarded_message{message_body = Body2} = parse_forwarded_message(Msg2), 1614: ?assert_equal(lists:nth(8, Msgs), Body2), 1615: #forwarded_message{message_body = Body3} = parse_forwarded_message(Msg3), 1616: ?assert_equal(lists:nth(11, Msgs), Body3), 1617: 1618: ok 1619: end, 1620: escalus:story(Config, [{alice, 1}, {bob, 1}], F). 1621: 1622: 1623: querying_for_all_messages_with_jid(Config) -> 1624: P = ?config(props, Config), 1625: F = fun(Alice) -> 1626: Pregenerated = ?config(pre_generated_msgs, Config), 1627: BWithJID = nick_to_jid(bob, Config), 1628: 1629: WithBob = [1 || {_, _, {JID, _, _}, _, _} <- Pregenerated, 1630: escalus_utils:jid_to_lower(JID) == BWithJID], 1631: 1632: CountWithBob = lists:sum(WithBob), 1633: escalus:send(Alice, stanza_filtered_by_jid_request(P, BWithJID)), 1634: assert_respond_size(CountWithBob, wait_archive_respond(Alice)), 1635: ok 1636: end, 1637: escalus:story(Config, [{alice, 1}], F). 1638: 1639: muc_querying_for_all_messages(Config) -> 1640: P = ?config(props, Config), 1641: F = fun(Alice) -> 1642: maybe_wait_for_archive(Config), 1643: 1644: Room = ?config(room, Config), 1645: MucMsgs = ?config(pre_generated_muc_msgs, Config), 1646: 1647: MucArchiveLen = length(MucMsgs), 1648: 1649: IQ = stanza_archive_request(P, <<>>), 1650: escalus:send(Alice, stanza_to_room(IQ, Room)), 1651: maybe_wait_for_archive(Config), 1652: assert_respond_size(MucArchiveLen, wait_archive_respond(Alice)), 1653: 1654: ok 1655: end, 1656: escalus:story(Config, [{alice, 1}], F). 1657: 1658: muc_querying_for_all_messages_with_jid(Config) -> 1659: P = ?config(props, Config), 1660: F = fun(Alice) -> 1661: Room = ?config(room, Config), 1662: BobNick = ?config(bob_nickname, Config), 1663: BWithJID = room_address(Room, BobNick), 1664: 1665: MucMsgs = ?config(pre_generated_muc_msgs, Config), 1666: WithJID = [1 || {_, _, {JID, _, _}, _, _} <- MucMsgs, JID == BWithJID], 1667: Len = lists:sum(WithJID), 1668: 1669: IQ = stanza_filtered_by_jid_request(P, BWithJID), 1670: escalus:send(Alice, stanza_to_room(IQ, Room)), 1671: Result = wait_archive_respond(Alice), 1672: 1673: assert_respond_size(Len, Result), 1674: ok 1675: end, 1676: escalus:story(Config, [{alice, 1}], F). 1677: 1678: muc_light_simple(Config) -> 1679: escalus:story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) -> 1680: Room = <<"testroom">>, 1681: given_muc_light_room(Room, Alice, []), 1682: 1683: M1 = when_muc_light_message_is_sent(Alice, Room, 1684: <<"Msg 1">>, <<"Id1">>), 1685: then_muc_light_message_is_received_by([Alice], M1), 1686: 1687: M2 = when_muc_light_message_is_sent(Alice, Room, 1688: <<"Message 2">>, <<"MyID2">>), 1689: then_muc_light_message_is_received_by([Alice], M2), 1690: 1691: Aff = when_muc_light_affiliations_are_set(Alice, Room, [{Bob, member}]), 1692: then_muc_light_affiliations_are_received_by([Alice, Bob], Aff), 1693: 1694: maybe_wait_for_archive(Config), 1695: when_archive_query_is_sent(Bob, muc_light_helper:room_bin_jid(Room), Config), 1696: ExpectedResponse = [{create, [{Alice, owner}]}, 1697: {muc_message, Room, Alice, <<"Msg 1">>}, 1698: {muc_message, Room, Alice, <<"Message 2">>}, 1699: {affiliations, [{Bob, member}]}], 1700: then_archive_response_is(Bob, ExpectedResponse, Config) 1701: end). 1702: 1703: muc_light_shouldnt_modify_pm_archive(Config) -> 1704: escalus:story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) -> 1705: Room = <<"testroom2">>, 1706: given_muc_light_room(Room, Alice, [{Bob, member}]), 1707: 1708: when_pm_message_is_sent(Alice, Bob, <<"private hi!">>), 1709: then_pm_message_is_received(Bob, <<"private hi!">>), 1710: 1711: maybe_wait_for_archive(Config), 1712: when_archive_query_is_sent(Alice, undefined, Config), 1713: then_archive_response_is(Alice, [{message, Alice, <<"private hi!">>}], Config), 1714: when_archive_query_is_sent(Bob, undefined, Config), 1715: then_archive_response_is(Bob, [{message, Alice, <<"private hi!">>}], Config), 1716: 1717: M1 = when_muc_light_message_is_sent(Alice, Room, 1718: <<"Msg 1">>, <<"Id 1">>), 1719: then_muc_light_message_is_received_by([Alice, Bob], M1), 1720: 1721: maybe_wait_for_archive(Config), 1722: when_archive_query_is_sent(Alice, muc_light_helper:room_bin_jid(Room), Config), 1723: then_archive_response_is(Alice, [{create, [{Alice, owner}, {Bob, member}]}, 1724: {muc_message, Room, Alice, <<"Msg 1">>}], Config), 1725: 1726: when_archive_query_is_sent(Alice, undefined, Config), 1727: then_archive_response_is(Alice, [{message, Alice, <<"private hi!">>}], Config), 1728: when_archive_query_is_sent(Bob, undefined, Config), 1729: then_archive_response_is(Bob, [{message, Alice, <<"private hi!">>}], Config) 1730: end). 1731: 1732: muc_light_stored_in_pm_if_allowed_to(Config) -> 1733: escalus:story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) -> 1734: Room = <<"testroom_pm">>, 1735: given_muc_light_room(Room, Alice, [{Bob, member}]), 1736: 1737: maybe_wait_for_archive(Config), 1738: AliceAffEvent = {affiliations, [{Alice, owner}]}, 1739: when_archive_query_is_sent(Alice, undefined, Config), 1740: then_archive_response_is(Alice, [AliceAffEvent], Config), 1741: BobAffEvent = {affiliations, [{Bob, member}]}, 1742: when_archive_query_is_sent(Bob, undefined, Config), 1743: then_archive_response_is(Bob, [BobAffEvent], Config), 1744: 1745: M1 = when_muc_light_message_is_sent(Alice, Room, <<"Msg 1">>, <<"Id 1">>), 1746: then_muc_light_message_is_received_by([Alice, Bob], M1), 1747: 1748: maybe_wait_for_archive(Config), 1749: MessageEvent = {muc_message, Room, Alice, <<"Msg 1">>}, 1750: when_archive_query_is_sent(Alice, undefined, Config), 1751: then_archive_response_is(Alice, [AliceAffEvent, MessageEvent], Config), 1752: when_archive_query_is_sent(Bob, undefined, Config), 1753: then_archive_response_is(Bob, [BobAffEvent, MessageEvent], Config) 1754: end). 1755: 1756: muc_light_chat_markers_are_archived_if_enabled(Config) -> 1757: escalus:story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) -> 1758: Room = <<"testroom_markers">>, 1759: given_muc_light_room(Room, Alice, [{Bob, member}]), 1760: 1761: %% Alice sends 3 chat markers 1762: MessageID = <<"some-fake-id">>, 1763: RoomJID = muc_light_helper:room_bin_jid(Room), 1764: lists:foreach( 1765: fun(Type) -> 1766: Marker1 = escalus_stanza:chat_marker(RoomJID, Type, MessageID), 1767: Marker2 = escalus_stanza:setattr(Marker1, <<"type">>, <<"groupchat">>), 1768: escalus:send(Alice, Marker2), 1769: escalus:wait_for_stanza(Alice), 1770: escalus:wait_for_stanza(Bob) 1771: end, [<<"received">>, <<"displayed">>, <<"acknowledged">>]), 1772: 1773: maybe_wait_for_archive(Config), 1774: when_archive_query_is_sent(Bob, muc_light_helper:room_bin_jid(Room), Config), 1775: ExpectedResponse = [ 1776: {create, [{Alice, owner}, {Bob, member}]}, 1777: {chat_marker, <<"received">>}, 1778: {chat_marker, <<"displayed">>}, 1779: {chat_marker, <<"acknowledged">>} 1780: ], 1781: then_archive_response_is(Bob, ExpectedResponse, Config) 1782: end). 1783: 1784: muc_light_chat_markers_are_not_archived_if_disabled(Config) -> 1785: escalus:story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) -> 1786: Room = <<"testroom_no_markers">>, 1787: given_muc_light_room(Room, Alice, [{Bob, member}]), 1788: 1789: %% Alice sends 3 chat markers 1790: MessageID = <<"some-fake-id">>, 1791: RoomJID = muc_light_helper:room_bin_jid(Room), 1792: lists:foreach( 1793: fun(Type) -> 1794: Marker1 = escalus_stanza:chat_marker(RoomJID, Type, MessageID), 1795: Marker2 = escalus_stanza:setattr(Marker1, <<"type">>, <<"groupchat">>), 1796: escalus:send(Alice, Marker2), 1797: escalus:wait_for_stanza(Alice), 1798: escalus:wait_for_stanza(Bob) 1799: end, [<<"received">>, <<"displayed">>, <<"acknowledged">>]), 1800: 1801: maybe_wait_for_archive(Config), 1802: when_archive_query_is_sent(Bob, muc_light_helper:room_bin_jid(Room), Config), 1803: ExpectedResponse = [{create, [{Alice, owner}, {Bob, member}]}], 1804: then_archive_response_is(Bob, ExpectedResponse, Config) 1805: end). 1806: 1807: retrieve_form_fields(ConfigIn) -> 1808: escalus_fresh:story(ConfigIn, [{alice, 1}], fun(Alice) -> 1809: P = ?config(props, ConfigIn), 1810: Namespace = get_prop(mam_ns, P), 1811: escalus:send(Alice, stanza_retrieve_form_fields(<<"q">>, Namespace)), 1812: Res = escalus:wait_for_stanza(Alice), 1813: escalus:assert(is_iq_with_ns, [Namespace], Res) 1814: end). 1815: 1816: archived(Config) -> 1817: P = ?config(props, Config), 1818: F = fun(Alice, Bob) -> 1819: %% Archive must be empty. 1820: %% Alice sends "OH, HAI!" to Bob. 1821: escalus:send(Alice, escalus_stanza:chat_to(Bob, <<"OH, HAI!">>)), 1822: 1823: %% Bob receives a message. 1824: Msg = escalus:wait_for_stanza(Bob), 1825: try 1826: StanzaId = exml_query:subelement(Msg, <<"stanza-id">>), 1827: %% JID of the archive (i.e. where the client would send queries to) 1828: By = exml_query:attr(StanzaId, <<"by">>), 1829: %% Attribute giving the message's UID within the archive. 1830: Id = exml_query:attr(StanzaId, <<"id">>), 1831: 1832: ?assert_equal(By, escalus_client:short_jid(Bob)), 1833: 1834: %% Bob calls archive. 1835: maybe_wait_for_archive(Config), 1836: escalus:send(Bob, stanza_archive_request(P, <<"q1">>)), 1837: [ArcMsg] = respond_messages(assert_respond_size(1, wait_archive_respond(Bob))), 1838: #forwarded_message{result_id=ArcId} = parse_forwarded_message(ArcMsg), 1839: ?assert_equal(Id, ArcId), 1840: ok 1841: catch Class:Reason:StackTrace -> 1842: ct:pal("Msg ~p", [Msg]), 1843: erlang:raise(Class, Reason, StackTrace) 1844: end 1845: end, 1846: %% Made fresh in init_per_testcase 1847: escalus:story(Config, [{alice, 1}, {bob, 1}], F). 1848: 1849: 1850: message_with_stanzaid(Config) -> 1851: F = fun(Alice, Bob) -> 1852: %% Archive must be empty. 1853: %% Alice sends "OH, HAI!" to Bob. 1854: escalus:send(Alice, escalus_stanza:chat_to(Bob, <<"OH, HAI!">>)), 1855: 1856: %% Bob receives a message. 1857: Msg = escalus:wait_for_stanza(Bob), 1858: 1859: ArcStanzaid = exml_query:subelement(Msg, <<"stanza-id">>), 1860: 1861: %% stanza-id has a namespace 'urn:xmpp:sid:0' 1862: <<"urn:xmpp:sid:0">> = exml_query:attr(ArcStanzaid, <<"xmlns">>), 1863: ok 1864: end, 1865: escalus:story(Config, [{alice, 1}, {bob, 1}], F). 1866: 1867: retract_message_on_stanza_id(Config) -> 1868: P = ?config(props, Config), 1869: F = fun(Alice, Bob) -> 1870: %% GIVEN Alice sends a message to Bob 1871: Body = <<"OH, HAI!">>, 1872: Msg = escalus_stanza:chat_to(Bob, Body), 1873: escalus:send(Alice, Msg), 1874: 1875: mam_helper:wait_for_archive_size(Alice, 1), 1876: escalus:send(Alice, stanza_archive_request(P, <<"q1">>)), 1877: Result = wait_archive_respond(Alice), 1878: [AliceCopyOfMessage] = respond_messages(Result), 1879: 1880: %% ... and Bob receives the message 1881: _RecvMsg = escalus:wait_for_stanza(Bob), 1882: 1883: %% WHEN Alice retracts the message 1884: ApplyToElement = apply_to_element([{retract_on, stanza_id}], AliceCopyOfMessage), 1885: RetractMsg = retraction_message(<<"chat">>, escalus_utils:get_jid(Bob), ApplyToElement), 1886: escalus:send(Alice, RetractMsg), 1887: 1888: %% THEN Bob receives the message with 'retract' ... 1889: RecvRetract = escalus:wait_for_stanza(Bob), 1890: ?assert_equal(ApplyToElement, exml_query:subelement(RecvRetract, <<"apply-to">>)), 1891: 1892: maybe_wait_for_archive(Config), 1893: 1894: %% ... and Alice and Bob have both messages in their archives 1895: escalus:send(Alice, stanza_archive_request(P, <<"q1">>)), 1896: check_archive_after_retraction(Config, Alice, ApplyToElement, Body), 1897: escalus:send(Bob, stanza_archive_request(P, <<"q2">>)), 1898: check_archive_after_retraction(Config, Bob, ApplyToElement, Body), 1899: 1900: ok 1901: end, 1902: escalus_fresh:story(Config, [{alice, 1}, {bob, 1}], F). 1903: 1904: retract_wrong_message(Config) -> 1905: retract_message([{origin_id, <<"wrong-id">>} | Config]). 1906: 1907: retract_message(Config) -> 1908: P = ?config(props, Config), 1909: F = fun(Alice, Bob) -> 1910: %% GIVEN Alice sends a message with 'origin-id' to Bob 1911: Body = <<"OH, HAI!">>, 1912: OriginIdElement = origin_id_element(origin_id()), 1913: Msg = #xmlel{children = Children} = escalus_stanza:chat_to(Bob, Body), 1914: escalus:send(Alice, Msg#xmlel{children = Children ++ [OriginIdElement]}), 1915: 1916: mam_helper:wait_for_archive_size(Alice, 1), 1917: escalus:send(Alice, stanza_archive_request(P, <<"q1">>)), 1918: Result = wait_archive_respond(Alice), 1919: [AliceCopyOfMessage] = respond_messages(Result), 1920: 1921: %% ... and Bob receives the message 1922: RecvMsg = escalus:wait_for_stanza(Bob), 1923: ?assert_equal(OriginIdElement, exml_query:subelement(RecvMsg, <<"origin-id">>)), 1924: 1925: %% WHEN Alice retracts the message 1926: ApplyToElement = apply_to_element(Config, AliceCopyOfMessage), 1927: RetractMsg = retraction_message(<<"chat">>, escalus_utils:get_jid(Bob), ApplyToElement), 1928: escalus:send(Alice, RetractMsg), 1929: 1930: %% THEN Bob receives the message with 'retract' ... 1931: RecvRetract = escalus:wait_for_stanza(Bob), 1932: ?assert_equal(ApplyToElement, exml_query:subelement(RecvRetract, <<"apply-to">>)), 1933: 1934: maybe_wait_for_archive(Config), 1935: 1936: %% ... and Alice and Bob have both messages in their archives 1937: escalus:send(Alice, stanza_archive_request(P, <<"q1">>)), 1938: check_archive_after_retraction(Config, Alice, ApplyToElement, Body), 1939: escalus:send(Bob, stanza_archive_request(P, <<"q2">>)), 1940: check_archive_after_retraction(Config, Bob, ApplyToElement, Body), 1941: 1942: ok 1943: end, 1944: escalus_fresh:story(Config, [{alice, 1}, {bob, 1}], F). 1945: 1946: filter_forwarded(Config) -> 1947: P = ?config(props, Config), 1948: F = fun(Alice, Bob) -> 1949: %% Alice sends "OH, HAI!" to Bob. 1950: escalus:send(Alice, escalus_stanza:chat_to(Bob, <<"OH, HAI!">>)), 1951: 1952: %% Bob receives a message. 1953: escalus:wait_for_stanza(Bob), 1954: mam_helper:wait_for_archive_size(Bob, 1), 1955: escalus:send(Bob, stanza_archive_request(P, <<"q1">>)), 1956: assert_respond_size(1, wait_archive_respond(Bob)), 1957: 1958: %% Check, that previous forwarded message was not archived. 1959: escalus:send(Bob, stanza_archive_request(P, <<"q2">>)), 1960: assert_respond_size(1, wait_archive_respond(Bob)), 1961: ok 1962: end, 1963: escalus_fresh:story(Config, [{alice, 1}, {bob, 1}], F). 1964: 1965: %% Ensure, that a offline message does not stored twice when delivered. 1966: offline_message(Config) -> 1967: Msg = <<"Is there anybody here?">>, 1968: P = ?config(props, Config), 1969: F = fun(Alice) -> 1970: %% Alice sends a message to Bob while bob is offline. 1971: escalus:send(Alice, 1972: escalus_stanza:chat_to(escalus_users:get_jid(Config, bob), Msg)), 1973: maybe_wait_for_archive(Config), 1974: ok 1975: end, 1976: escalus:story(Config, [{alice, 1}], F), 1977: 1978: %% Bob logs in 1979: Bob = login_send_presence(Config, bob), 1980: 1981: %% If mod_offline is enabled, then an offline message 1982: %% will be delivered automatically. 1983: 1984: %% He receives his initial presence and the message. 1985: escalus:wait_for_stanzas(Bob, 2, 1000), 1986: 1987: %% Bob checks his archive. 1988: escalus:send(Bob, stanza_archive_request(P, <<"q1">>)), 1989: ArcMsgs = respond_messages(wait_archive_respond(Bob)), 1990: assert_only_one_of_many_is_equal(ArcMsgs, Msg), 1991: 1992: escalus_client:stop(Config, Bob). 1993: 1994: nostore_hint(Config) -> 1995: Msg = <<"So secret">>, 1996: P = ?config(props, Config), 1997: F = fun(Alice, Bob) -> 1998: %% Alice sends a message to Bob with a hint. 1999: escalus:send(Alice, 2000: add_nostore_hint(escalus_stanza:chat_to(Bob, Msg))), 2001: maybe_wait_for_archive(Config), 2002: escalus:wait_for_stanzas(Bob, 1, 1000), 2003: 2004: %% Bob checks his archive. 2005: escalus:send(Bob, stanza_archive_request(P, <<"q1">>)), 2006: ArcMsgs = respond_messages(wait_archive_respond(Bob)), 2007: assert_not_stored(ArcMsgs, Msg), 2008: ok 2009: end, 2010: escalus_fresh:story(Config, [{alice, 1}, {bob, 1}], F). 2011: 2012: muc_message_with_stanzaid(Config) -> 2013: F = fun(Alice, Bob) -> 2014: Room = ?config(room, Config), 2015: RoomAddr = room_address(Room), 2016: Text = <<"Hi, Bob!">>, 2017: escalus:send(Alice, stanza_muc_enter_room(Room, nick(Alice))), 2018: escalus:send(Bob, stanza_muc_enter_room(Room, nick(Bob))), 2019: 2020: %% Bob received presences. 2021: escalus:wait_for_stanzas(Bob, 2), 2022: 2023: %% Bob received the room's subject. 2024: escalus:wait_for_stanzas(Bob, 1), 2025: 2026: %% Alice sends another message to Bob. 2027: %% The message is not archived by the room. 2028: escalus:send(Alice, escalus_stanza:chat_to(Bob, <<"OH, HAI!">>)), 2029: escalus:assert(is_message, escalus:wait_for_stanza(Bob)), 2030: 2031: %% Alice sends to the chat room. 2032: escalus:send(Alice, escalus_stanza:groupchat_to(RoomAddr, Text)), 2033: 2034: %% Bob received the message "Hi, Bob!". 2035: %% This message will be archived (by alicesroom@localhost). 2036: %% User's archive is disabled (i.e. bob@localhost). 2037: BobMsg = escalus:wait_for_stanza(Bob), 2038: escalus:assert(is_message, BobMsg), 2039: ArcStanzaid = exml_query:subelement(BobMsg, <<"stanza-id">>), 2040: 2041: %% stanza-id has a namespace 'urn:xmpp:sid:0' 2042: Xmlns = exml_query:attr(ArcStanzaid, <<"xmlns">>), 2043: ?assert_equal(Xmlns, <<"urn:xmpp:sid:0">>), 2044: ok 2045: end, 2046: escalus:story(Config, [{alice, 1}, {bob, 1}], F). 2047: 2048: retract_muc_message_on_stanza_id(Config) -> 2049: retract_muc_message([{retract_on, stanza_id} | Config]). 2050: 2051: retract_wrong_muc_message(Config) -> 2052: retract_muc_message([{origin_id, <<"wrong-id">>} | Config]). 2053: 2054: retract_muc_message(Config) -> 2055: P = ?config(props, Config), 2056: F = fun(Alice, Bob) -> 2057: Room = ?config(room, Config), 2058: RoomAddr = room_address(Room), 2059: escalus:send(Alice, stanza_muc_enter_room(Room, nick(Alice))), 2060: escalus:send(Bob, stanza_muc_enter_room(Room, nick(Bob))), 2061: %% Bob received presences. 2062: escalus:wait_for_stanzas(Bob, 2), 2063: %% Bob received the room's subject. 2064: escalus:wait_for_stanzas(Bob, 1), 2065: %% Alice receives all the messages Bob did as well 2066: escalus:wait_for_stanzas(Alice, 3), 2067: 2068: %% GIVEN Alice sends a message with 'origin-id' to the chat room ... 2069: Body = <<"Hi, Bob!">>, 2070: OriginIdElement = origin_id_element(origin_id()), 2071: Msg = #xmlel{children = Children} = escalus_stanza:groupchat_to(RoomAddr, Body), 2072: escalus:send(Alice, Msg#xmlel{children = Children ++ [OriginIdElement]}), 2073: 2074: AliceCopyOfMessage = escalus:wait_for_stanza(Alice), 2075: 2076: %% ... and Bob receives the message 2077: RecvMsg = escalus:wait_for_stanza(Bob), 2078: ?assert_equal(OriginIdElement, exml_query:subelement(RecvMsg, <<"origin-id">>)), 2079: 2080: %% WHEN Alice retracts the message 2081: ApplyToElement = apply_to_element(Config, AliceCopyOfMessage), 2082: RetractMsg = retraction_message(<<"groupchat">>, RoomAddr, ApplyToElement), 2083: escalus:send(Alice, RetractMsg), 2084: 2085: %% THEN Bob receives the message with 'retract' ... 2086: RecvRetract = escalus:wait_for_stanza(Bob), 2087: ?assert_equal(ApplyToElement, exml_query:subelement(RecvRetract, <<"apply-to">>)), 2088: 2089: maybe_wait_for_archive(Config), 2090: 2091: %% ... and finds both messages in the room archive 2092: escalus:send(Bob, stanza_to_room(stanza_archive_request(P, <<"q1">>), Room)), 2093: check_archive_after_retraction(Config, Bob, ApplyToElement, Body), 2094: 2095: ok 2096: end, 2097: escalus:story(Config, [{alice, 1}, {bob, 1}], F). 2098: 2099: muc_archive_request(Config) -> 2100: P = ?config(props, Config), 2101: F = fun(Alice, Bob) -> 2102: Room = ?config(room, Config), 2103: RoomAddr = room_address(Room), 2104: Text = <<"Hi, Bob!">>, 2105: escalus:send(Alice, stanza_muc_enter_room(Room, nick(Alice))), 2106: escalus:send(Bob, stanza_muc_enter_room(Room, nick(Bob))), 2107: 2108: %% Bob received presences. 2109: escalus:wait_for_stanzas(Bob, 2), 2110: 2111: %% Bob received the room's subject. 2112: escalus:wait_for_stanzas(Bob, 1), 2113: 2114: %% Alice sends another message to Bob. 2115: %% The message is not archived by the room. 2116: escalus:send(Alice, escalus_stanza:chat_to(Bob, <<"OH, HAI!">>)), 2117: escalus:assert(is_message, escalus:wait_for_stanza(Bob)), 2118: 2119: %% Alice sends to the chat room. 2120: escalus:send(Alice, escalus_stanza:groupchat_to(RoomAddr, Text)), 2121: 2122: %% Bob received the message "Hi, Bob!". 2123: %% This message will be archived (by alicesroom@localhost). 2124: %% User's archive is disabled (i.e. bob@localhost). 2125: BobMsg = escalus:wait_for_stanza(Bob), 2126: escalus:assert(is_message, BobMsg), 2127: Arc = exml_query:subelement(BobMsg, <<"stanza-id">>), 2128: %% JID of the archive (i.e. where the client would send queries to) 2129: By = exml_query:attr(Arc, <<"by">>), 2130: %% Attribute giving the message's UID within the archive. 2131: Id = exml_query:attr(Arc, <<"id">>), 2132: 2133: maybe_wait_for_archive(Config), 2134: 2135: %% Bob requests the room's archive. 2136: escalus:send(Bob, stanza_to_room(stanza_archive_request(P, <<"q1">>), Room)), 2137: [ArcMsg] = respond_messages(assert_respond_size(1, wait_archive_respond(Bob))), 2138: #forwarded_message{result_id=ArcId, message_body=ArcMsgBody, 2139: message_to=MsgTo, message_from=MsgFrom} = 2140: parse_forwarded_message(ArcMsg), 2141: %% XEP: the 'to' of the forwarded stanza MUST be empty 2142: %% However, Smack crashes if it is present, so it is removed 2143: ?assert_equal_extra(undefined, MsgTo, message_to), 2144: 2145: %% XEP: the 'from' MUST be the occupant JID of the sender of the archived message 2146: ?assert_equal_extra(escalus_utils:jid_to_lower(room_address(Room, nick(Alice))), 2147: escalus_utils:jid_to_lower(MsgFrom), message_from), 2148: 2149: ?assert_equal(Text, ArcMsgBody), 2150: ?assert_equal(ArcId, Id), 2151: ?assert_equal(escalus_utils:jid_to_lower(RoomAddr), By), 2152: ?assert_equal_extra(true, has_x_user_element(ArcMsg), 2153: [{forwarded_message, ArcMsg}]), 2154: ok 2155: end, 2156: escalus:story(Config, [{alice, 1}, {bob, 1}], F). 2157: 2158: muc_multiple_devices(Config) -> 2159: P = ?config(props, Config), 2160: F = fun(Alice1, Alice2, Bob) -> 2161: Room = ?config(room, Config), 2162: RoomAddr = room_address(Room), 2163: Text = <<"Hi, Bob!">>, 2164: %% You should use an unique nick for each device. 2165: escalus:send(Alice1, stanza_muc_enter_room(Room, <<"alice_1">>)), 2166: escalus:send(Alice2, stanza_muc_enter_room(Room, <<"alice_2">>)), 2167: escalus:send(Bob, stanza_muc_enter_room(Room, <<"bob">>)), 2168: 2169: %% Alice received presences. 2170: escalus:wait_for_stanzas(Alice1, 3), 2171: escalus:wait_for_stanzas(Alice2, 3), 2172: 2173: %% Bob received presences. 2174: escalus:wait_for_stanzas(Bob, 3), 2175: 2176: %% Bob received the room's subject. 2177: escalus:wait_for_stanzas(Bob, 1), 2178: 2179: %% Alice received the room's subject. 2180: escalus:wait_for_stanzas(Alice1, 1), 2181: escalus:wait_for_stanzas(Alice2, 1), 2182: 2183: %% Alice sends to the chat room. 2184: escalus:send(Alice1, escalus_stanza:groupchat_to(RoomAddr, Text)), 2185: 2186: %% Alice receives her own message. 2187: Alice1Msg = escalus:wait_for_stanza(Alice1), 2188: escalus:assert(is_message, Alice1Msg), 2189: 2190: Alice2Msg = escalus:wait_for_stanza(Alice2), 2191: escalus:assert(is_message, Alice2Msg), 2192: 2193: Alice1Arc = exml_query:subelement(Alice1Msg, <<"stanza-id">>), 2194: Alice2Arc = exml_query:subelement(Alice2Msg, <<"stanza-id">>), 2195: ?assert_equal(Alice1Arc, Alice2Arc), 2196: 2197: %% Bob received the message "Hi, Bob!". 2198: %% This message will be archived (by alicesroom@localhost). 2199: %% User's archive is disabled (i.e. bob@localhost). 2200: BobMsg = escalus:wait_for_stanza(Bob), 2201: escalus:assert(is_message, BobMsg), 2202: Arc = exml_query:subelement(BobMsg, <<"stanza-id">>), 2203: %% JID of the archive (i.e. where the client would send queries to) 2204: By = exml_query:attr(Arc, <<"by">>), 2205: %% Attribute giving the message's UID within the archive. 2206: Id = exml_query:attr(Arc, <<"id">>), 2207: 2208: ?assert_equal(Alice1Arc, Arc), 2209: 2210: %% Bob requests the room's archive. 2211: 2212: maybe_wait_for_archive(Config), 2213: 2214: escalus:send(Bob, stanza_to_room(stanza_archive_request(P, <<"q1">>), Room)), 2215: [ArcMsg] = respond_messages(assert_respond_size(1, wait_archive_respond(Bob))), 2216: #forwarded_message{result_id=ArcId, message_body=ArcMsgBody} = 2217: parse_forwarded_message(ArcMsg), 2218: ?assert_equal(Text, ArcMsgBody), 2219: ?assert_equal(ArcId, Id), 2220: ?assert_equal(escalus_utils:jid_to_lower(RoomAddr), By), 2221: ok 2222: end, 2223: escalus:story(Config, [{alice, 2}, {bob, 1}], F). 2224: 2225: muc_protected_message(Config) -> 2226: P = ?config(props, Config), 2227: F = fun(Alice, Bob) -> 2228: Room = ?config(room, Config), 2229: Text = <<"Hi, Bob!">>, 2230: escalus:send(Alice, stanza_muc_enter_room(Room, nick(Alice))), 2231: escalus:send(Bob, stanza_muc_enter_room(Room, nick(Bob))), 2232: 2233: %% Bob received presences. 2234: escalus:wait_for_stanzas(Bob, 2), 2235: 2236: %% Bob received the room's subject. 2237: escalus:wait_for_stanzas(Bob, 1), 2238: 2239: %% Alice sends to Bob, using his occupant JID. 2240: %% This message will not be put into room's history. 2241: Msg = escalus_stanza:chat_to(room_address(Room, nick(Bob)), Text), 2242: escalus:send(Alice, Msg), 2243: 2244: %% Bob received the message "Hi, Bob!". 2245: BobMsg = escalus:wait_for_stanza(Bob), 2246: escalus:assert(is_message, BobMsg), 2247: 2248: BobArchiveAddr = escalus_client:short_jid(Bob), 2249: ArchivedBy = [exml_query:attr(Arc, <<"by">>) 2250: || Arc <- BobMsg#xmlel.children, 2251: Arc#xmlel.name =:= <<"archived">>, 2252: BobArchiveAddr =/= exml_query:attr(Arc, <<"by">>)], 2253: ?assert_equal([], ArchivedBy), 2254: 2255: %% Bob requests the room's archive. 2256: escalus:send(Bob, stanza_to_room(stanza_archive_request(P, <<"q1">>), Room)), 2257: assert_respond_size(0, wait_archive_respond(Bob)), 2258: ok 2259: end, 2260: escalus:story(Config, [{alice, 1}, {bob, 1}], F). 2261: 2262: muc_deny_protected_room_access(Config) -> 2263: P = ?config(props, Config), 2264: F = fun(Alice, Bob) -> 2265: Room = ?config(room, Config), 2266: RoomAddr = room_address(Room), 2267: Text = <<"Hi, Bob!">>, 2268: escalus:send(Alice, stanza_muc_enter_room(Room, nick(Alice))), 2269: escalus:send(Bob, stanza_muc_enter_room(Room, nick(Bob))), 2270: 2271: %% mod_muc returns error presence. 2272: Err1 = escalus:wait_for_stanza(Bob), 2273: escalus_assert:is_error(Err1, <<"auth">>, <<"not-authorized">>), 2274: 2275: %% Alice sends to the chat room. 2276: escalus:send(Alice, escalus_stanza:groupchat_to(RoomAddr, Text)), 2277: 2278: %% Bob requests the room's archive. 2279: escalus:send(Bob, stanza_to_room(stanza_archive_request(P, <<"q1">>), Room)), 2280: Err2 = escalus:wait_for_stanza(Bob), 2281: %% mod_mam_muc returns error iq. 2282: escalus:assert(is_error, [<<"cancel">>, <<"not-allowed">>], Err2), 2283: ok 2284: end, 2285: escalus:story(Config, [{alice, 1}, {bob, 1}], F). 2286: 2287: %% @doc Allow access to non-in-room users who able to connect 2288: muc_allow_access_to_owner(Config) -> 2289: P = ?config(props, Config), 2290: F = fun(Alice, _Bob) -> 2291: Room = ?config(room, Config), 2292: _RoomAddr = room_address(Room), 2293: 2294: %% Alice (not in room) requests the room's archive. 2295: escalus:send(Alice, stanza_to_room(stanza_archive_request(P, <<"q1">>), Room)), 2296: %% mod_mam_muc returns result. 2297: assert_respond_size(0, wait_archive_respond(Alice)), 2298: ok 2299: end, 2300: escalus:story(Config, [{alice, 1}, {bob, 1}], F). 2301: 2302: muc_delete_x_user_in_anon_rooms(Config) -> 2303: escalus:story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) -> 2304: {Room, RoomAddr} = enter_room(Config, [Alice, Bob]), 2305: Text = <<"Hi all!">>, 2306: 2307: %% Bob received presences. 2308: escalus:wait_for_stanzas(Bob, 2), 2309: 2310: %% Bob received the room's subject. 2311: escalus:wait_for_stanzas(Bob, 1), 2312: 2313: %% Alice sends to the chat room. 2314: escalus:send(Alice, escalus_stanza:groupchat_to(RoomAddr, Text)), 2315: 2316: %% Bob receives the message. 2317: escalus:assert(is_message, escalus:wait_for_stanza(Bob)), 2318: 2319: maybe_wait_for_archive(Config), 2320: Props = ?config(props, Config), 2321: 2322: %% Bob requests the room's archive. 2323: escalus:send(Bob, stanza_to_room(stanza_archive_request(Props, <<"q1">>), Room)), 2324: 2325: %% mod_mam_muc returns result. 2326: [ArcMsg] = respond_messages(assert_respond_size(1, wait_archive_respond(Bob))), 2327: 2328: ?assert_equal_extra(false, has_x_user_element(ArcMsg), 2329: [{forwarded_message, ArcMsg}]), 2330: ok 2331: end). 2332: 2333: muc_show_x_user_to_moderators_in_anon_rooms(Config) -> 2334: escalus:story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) -> 2335: {Room, RoomAddr} = enter_room(Config, [Alice, Bob]), 2336: Text = <<"What a lovely day!">>, 2337: 2338: %% Alice received presences. 2339: escalus:wait_for_stanzas(Alice, 2), 2340: 2341: %% Alice received the room's subject. 2342: escalus:wait_for_stanzas(Alice, 1), 2343: 2344: %% Bob sends to the chat room. 2345: escalus:send(Bob, escalus_stanza:groupchat_to(RoomAddr, Text)), 2346: 2347: %% Alice receives the message. 2348: escalus:assert(is_message, escalus:wait_for_stanza(Alice)), 2349: 2350: maybe_wait_for_archive(Config), 2351: Props = ?config(props, Config), 2352: 2353: %% Alice requests the room's archive. 2354: escalus:send(Alice, stanza_to_room(stanza_archive_request(Props, <<"q1">>), Room)), 2355: 2356: %% mod_mam_muc returns result. 2357: [ArcMsg] = respond_messages(assert_respond_size(1, wait_archive_respond(Alice))), 2358: 2359: ?assert_equal_extra(true, has_x_user_element(ArcMsg), 2360: [{forwarded_message, ArcMsg}]), 2361: ok 2362: end). 2363: 2364: muc_show_x_user_for_your_own_messages_in_anon_rooms(Config) -> 2365: escalus:story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) -> 2366: {Room, RoomAddr} = enter_room(Config, [Alice, Bob]), 2367: Text = <<"How are you?">>, 2368: 2369: %% Bob received presences. 2370: escalus:wait_for_stanzas(Bob, 2), 2371: 2372: %% Bob received the room's subject. 2373: escalus:wait_for_stanzas(Bob, 1), 2374: 2375: %% Bob sends to the chat room. 2376: escalus:send(Bob, escalus_stanza:groupchat_to(RoomAddr, Text)), 2377: 2378: %% Bob receives the message. 2379: escalus:assert(is_message, escalus:wait_for_stanza(Bob)), 2380: 2381: maybe_wait_for_archive(Config), 2382: Props = ?config(props, Config), 2383: 2384: %% Bob requests the room's archive. 2385: escalus:send(Bob, stanza_to_room(stanza_archive_request(Props, <<"q1">>), Room)), 2386: 2387: %% mod_mam_muc returns result. 2388: [ArcMsg] = respond_messages(assert_respond_size(1, wait_archive_respond(Bob))), 2389: 2390: ?assert_equal_extra(true, has_x_user_element(ArcMsg), 2391: [{forwarded_message, ArcMsg}]), 2392: ok 2393: end). 2394: 2395: %% @doc Querying the archive for all messages in a certain timespan. 2396: range_archive_request(Config) -> 2397: P = ?config(props, Config), 2398: F = fun(Alice) -> 2399: %% Send 2400: %% <iq type='get'> 2401: %% <query xmlns='urn:xmpp:mam:tmp'> 2402: %% <start>2010-06-07T00:00:00Z</start> 2403: %% <end>2010-07-07T13:23:54Z</end> 2404: %% </query> 2405: %% </iq> 2406: escalus:send(Alice, stanza_date_range_archive_request(P)), 2407: IQ = escalus:wait_for_stanza(Alice, 5000), 2408: escalus:assert(is_iq_result, IQ), 2409: ok 2410: end, 2411: escalus_fresh:story(Config, [{alice, 1}], F). 2412: 2413: range_archive_request_not_empty(Config) -> 2414: P = ?config(props, Config), 2415: F = fun(Alice) -> 2416: Msgs = ?config(pre_generated_msgs, Config), 2417: [_, _, StartMsg, StopMsg | _] = Msgs, 2418: {{StartMsgId, _}, _, _, _, _StartMsgPacket} = StartMsg, 2419: {{StopMsgId, _}, _, _, _, _StopMsgPacket} = StopMsg, 2420: {StartMicro, _} = rpc_apply(mod_mam_utils, decode_compact_uuid, [StartMsgId]), 2421: {StopMicro, _} = rpc_apply(mod_mam_utils, decode_compact_uuid, [StopMsgId]), 2422: StartTime = make_iso_time(StartMicro), 2423: StopTime = make_iso_time(StopMicro), 2424: %% Send 2425: %% <iq type='get'> 2426: %% <query xmlns='urn:xmpp:mam:tmp'> 2427: %% <start>StartTime</start> 2428: %% <end>StopTime</end> 2429: %% </query> 2430: %% </iq> 2431: escalus:send(Alice, stanza_date_range_archive_request_not_empty(P, StartTime, StopTime)), 2432: %% Receive two messages and IQ 2433: Result = wait_archive_respond(Alice), 2434: IQ = respond_iq(Result), 2435: [M1, M2|_] = respond_messages(Result), 2436: escalus:assert(is_iq_result, IQ), 2437: #forwarded_message{delay_stamp=Stamp1} = parse_forwarded_message(M1), 2438: #forwarded_message{delay_stamp=Stamp2} = parse_forwarded_message(M2), 2439: ?assert_equal(list_to_binary(StartTime), Stamp1), 2440: ?assert_equal(list_to_binary(StopTime), Stamp2), 2441: ok 2442: end, 2443: %% Made fresh in init_per_testcase 2444: escalus:story(Config, [{alice, 1}], F). 2445: 2446: %% @doc A query using Result Set Management. 2447: %% See also `#rsm_in.max'. 2448: limit_archive_request(Config) -> 2449: P = ?config(props, Config), 2450: F = fun(Alice) -> 2451: %% Send 2452: %% <iq type='get' id='q29302'> 2453: %% <query xmlns='urn:xmpp:mam:tmp'> 2454: %% <start>2010-08-07T00:00:00Z</start> 2455: %% <set xmlns='http://jabber.org/protocol/rsm'> 2456: %% <limit>10</limit> 2457: %% </set> 2458: %% </query> 2459: %% </iq> 2460: escalus:send(Alice, stanza_limit_archive_request(P)), 2461: Result = wait_archive_respond(Alice), 2462: Msgs = respond_messages(Result), 2463: IQ = respond_iq(Result), 2464: escalus:assert(is_iq_result, IQ), 2465: 10 = length(Msgs), 2466: ok 2467: end, 2468: %% Made fresh in init_per_testcase 2469: escalus:story(Config, [{alice, 1}], F). 2470: 2471: archive_chat_markers(Config) -> 2472: P = ?config(props, Config), 2473: F = fun(Alice, Bob) -> 2474: %% Alice sends markable message to Bob 2475: Message = escalus_stanza:markable( 2476: escalus_stanza:chat_to(Bob, <<"Hello, Bob!">>) 2477: ), 2478: MessageID = escalus_stanza:id(), 2479: escalus:send(Alice, escalus_stanza:set_id(Message, MessageID)), 2480: escalus:wait_for_stanza(Bob), 2481: 2482: %% Bob sends 3 chat markers 2483: Marker1 = escalus_stanza:chat_marker(Alice, <<"received">>, 2484: MessageID), 2485: Marker2 = escalus_stanza:chat_marker(Alice, <<"displayed">>, 2486: MessageID), 2487: Marker3 = escalus_stanza:chat_marker(Alice, <<"acknowledged">>, 2488: MessageID), 2489: escalus:send(Bob, Marker1), 2490: escalus:send(Bob, Marker2), 2491: escalus:send(Bob, Marker3), 2492: escalus:wait_for_stanzas(Alice, 3), 2493: 2494: %% Alice queries MAM 2495: maybe_wait_for_archive(Config), 2496: escalus:send(Alice, stanza_archive_request(P, <<"q1">>)), 2497: Result = wait_archive_respond(Alice), 2498: 2499: %% archived message + 3 markers 2500: assert_respond_size(1 + 3, Result), 2501: assert_respond_query_id(P, <<"q1">>, parse_result_iq(Result)) 2502: end, 2503: escalus:story(Config, [{alice, 1}, {bob, 1}], F). 2504: 2505: dont_archive_chat_markers(Config) -> 2506: P = ?config(props, Config), 2507: F = fun(Alice, Bob) -> 2508: %% Alice sends markable message to Bob 2509: Message = escalus_stanza:markable( 2510: escalus_stanza:chat_to(Bob, <<"Hello, Bob!">>) 2511: ), 2512: MessageID = escalus_stanza:id(), 2513: escalus:send(Alice, escalus_stanza:set_id(Message, MessageID)), 2514: escalus:wait_for_stanza(Bob), 2515: 2516: %% Bob sends 3 chat markers which also contain non-archivable elements 2517: Marker = #xmlel{children = Children} = 2518: escalus_stanza:chat_marker(Alice, <<"received">>, MessageID), 2519: ResultEl = #xmlel{name = <<"result">>}, 2520: DelayEl = #xmlel{name = <<"delay">>}, 2521: NoStoreEl = mam_helper:hint_elem(no_store), 2522: 2523: escalus:send(Bob, Marker#xmlel{children = [ResultEl|Children]}), 2524: escalus:send(Bob, Marker#xmlel{children = [DelayEl|Children]}), 2525: escalus:send(Bob, Marker#xmlel{children = [NoStoreEl|Children]}), 2526: escalus:wait_for_stanzas(Alice, 3), 2527: 2528: %% Alice queries MAM 2529: maybe_wait_for_archive(Config), 2530: escalus:send(Alice, stanza_archive_request(P, <<"q1">>)), 2531: Result = wait_archive_respond(Alice), 2532: 2533: %% archived message (no archived markers) 2534: assert_respond_size(1, Result), 2535: assert_respond_query_id(P, <<"q1">>, parse_result_iq(Result)) 2536: end, 2537: escalus:story(Config, [{alice, 1}, {bob, 1}], F). 2538: 2539: pagination_empty_rset(Config) -> 2540: P = ?config(props, Config), 2541: F = fun(Alice) -> 2542: %% Get the first page of size 5. 2543: RSM = #rsm_in{max=0}, 2544: 2545: rsm_send(Config, Alice, 2546: stanza_page_archive_request(P, <<"empty_rset">>, RSM)), 2547: wait_empty_rset(Alice, 15) 2548: end, 2549: parallel_story(Config, [{alice, 1}], F). 2550: 2551: pagination_first5(Config) -> 2552: P = ?config(props, Config), 2553: F = fun(Alice) -> 2554: %% Get the first page of size 5. 2555: RSM = #rsm_in{max=5}, 2556: rsm_send(Config, Alice, 2557: stanza_page_archive_request(P, <<"first5">>, RSM)), 2558: wait_message_range(Alice, 1, 5), 2559: ok 2560: end, 2561: parallel_story(Config, [{alice, 1}], F). 2562: 2563: pagination_first0(Config) -> 2564: P = ?config(props, Config), 2565: F = fun(Alice) -> 2566: %% Get the first page of size 0. 2567: RSM = #rsm_in{max=0}, 2568: rsm_send(Config, Alice, 2569: stanza_page_archive_request(P, <<"first5">>, RSM)), 2570: wait_empty_rset(Alice, 15), 2571: ok 2572: end, 2573: parallel_story(Config, [{alice, 1}], F). 2574: 2575: pagination_first5_opt_count(Config) -> 2576: P = ?config(props, Config), 2577: F = fun(Alice) -> 2578: %% Get the first page of size 5. 2579: RSM = #rsm_in{max=5}, 2580: rsm_send(Config, Alice, 2581: stanza_page_archive_request(P, <<"first5_opt">>, RSM)), 2582: wait_message_range(Alice, 1, 5), 2583: ok 2584: end, 2585: parallel_story(Config, [{alice, 1}], F). 2586: 2587: pagination_first25_opt_count_all(Config) -> 2588: P = ?config(props, Config), 2589: F = fun(Alice) -> 2590: %% Get the first page of size 25. 2591: RSM = #rsm_in{max=25}, 2592: rsm_send(Config, Alice, 2593: stanza_page_archive_request(P, <<"first25_opt_all">>, RSM)), 2594: wait_message_range(Alice, 1, 15), 2595: ok 2596: end, 2597: parallel_story(Config, [{alice, 1}], F). 2598: 2599: pagination_last5(Config) -> 2600: P = ?config(props, Config), 2601: F = fun(Alice) -> 2602: %% Get the last page of size 5. 2603: RSM = #rsm_in{max=5, direction=before}, 2604: rsm_send(Config, Alice, 2605: stanza_page_archive_request(P, <<"last5">>, RSM)), 2606: wait_message_range(Alice, 11, 15), 2607: ok 2608: end, 2609: parallel_story(Config, [{alice, 1}], F). 2610: 2611: pagination_last0(Config) -> 2612: P = ?config(props, Config), 2613: F = fun(Alice) -> 2614: %% Get the last page of size 0. 2615: RSM = #rsm_in{max=0, direction=before}, 2616: rsm_send(Config, Alice, 2617: stanza_page_archive_request(P, <<"last0">>, RSM)), 2618: wait_empty_rset(Alice, 15), 2619: ok 2620: end, 2621: parallel_story(Config, [{alice, 1}], F). 2622: 2623: pagination_offset5(Config) -> 2624: P = ?config(props, Config), 2625: F = fun(Alice) -> 2626: %% Skip 5 messages, get 5 messages. 2627: RSM = #rsm_in{max=5, index=5}, 2628: rsm_send(Config, Alice, 2629: stanza_page_archive_request(P, <<"offset5">>, RSM)), 2630: wait_message_range(Alice, 6, 10), 2631: ok 2632: end, 2633: parallel_story(Config, [{alice, 1}], F). 2634: 2635: pagination_offset5_max0(Config) -> 2636: P = ?config(props, Config), 2637: F = fun(Alice) -> 2638: %% Skip 5 messages, get 0 messages. 2639: RSM = #rsm_in{max=0, index=5}, 2640: rsm_send(Config, Alice, 2641: stanza_page_archive_request(P, <<"offset0_max5">>, RSM)), 2642: wait_empty_rset(Alice, 15), 2643: ok 2644: end, 2645: parallel_story(Config, [{alice, 1}], F). 2646: 2647: pagination_last5_opt_count(Config) -> 2648: P = ?config(props, Config), 2649: F = fun(Alice) -> 2650: %% Get the last page of size 5. 2651: RSM = #rsm_in{max=5, direction=before, opt_count=true}, 2652: rsm_send(Config, Alice, 2653: stanza_page_archive_request(P, <<"last5_opt">>, RSM)), 2654: wait_message_range(Alice, 11, 15), 2655: ok 2656: end, 2657: parallel_story(Config, [{alice, 1}], F). 2658: 2659: pagination_last25_opt_count_all(Config) -> 2660: P = ?config(props, Config), 2661: F = fun(Alice) -> 2662: %% Get the last page of size 25. 2663: RSM = #rsm_in{max=25, direction=before, opt_count=true}, 2664: rsm_send(Config, Alice, 2665: stanza_page_archive_request(P, <<"last25_opt_all">>, RSM)), 2666: wait_message_range(Alice, 1, 15), 2667: ok 2668: end, 2669: parallel_story(Config, [{alice, 1}], F). 2670: 2671: pagination_offset5_opt_count(Config) -> 2672: P = ?config(props, Config), 2673: F = fun(Alice) -> 2674: %% Skip 5 messages, get 5 messages. 2675: RSM = #rsm_in{max=5, index=5, opt_count=true}, 2676: rsm_send(Config, Alice, 2677: stanza_page_archive_request(P, <<"last5_opt">>, RSM)), 2678: wait_message_range(Alice, 6, 10), 2679: ok 2680: end, 2681: parallel_story(Config, [{alice, 1}], F). 2682: 2683: pagination_offset5_opt_count_all(Config) -> 2684: P = ?config(props, Config), 2685: F = fun(Alice) -> 2686: %% Skip 5 messages, get 25 messages (only 10 are available). 2687: RSM = #rsm_in{max=25, index=5, opt_count=true}, 2688: rsm_send(Config, Alice, 2689: stanza_page_archive_request(P, <<"last5_opt_all">>, RSM)), 2690: wait_message_range(Alice, 6, 15), 2691: ok 2692: end, 2693: parallel_story(Config, [{alice, 1}], F). 2694: 2695: 2696: pagination_before10(Config) -> 2697: P = ?config(props, Config), 2698: F = fun(Alice) -> 2699: %% Get the last page of size 5. 2700: RSM = #rsm_in{max=5, direction=before, id=message_id(10, Config)}, 2701: rsm_send(Config, Alice, 2702: stanza_page_archive_request(P, <<"before10">>, RSM)), 2703: wait_message_range(Alice, 5, 9), 2704: ok 2705: end, 2706: parallel_story(Config, [{alice, 1}], F). 2707: 2708: pagination_simple_before10(Config) -> 2709: P = ?config(props, Config), 2710: F = fun(Alice) -> 2711: %% Get the last page of size 5. 2712: RSM = #rsm_in{max=5, direction=before, id=message_id(10, Config), simple=true}, 2713: rsm_send(Config, Alice, 2714: stanza_page_archive_request(P, <<"before10">>, RSM)), 2715: %% wait_message_range(Client, TotalCount, Offset, FromN, ToN), 2716: wait_message_range(Alice, undefined, undefined, 5, 9), 2717: ok 2718: end, 2719: parallel_story(Config, [{alice, 1}], F). 2720: 2721: pagination_after10(Config) -> 2722: P = ?config(props, Config), 2723: F = fun(Alice) -> 2724: %% Get the last page of size 5. 2725: RSM = #rsm_in{max=5, direction='after', id=message_id(10, Config)}, 2726: rsm_send(Config, Alice, 2727: stanza_page_archive_request(P, <<"after10">>, RSM)), 2728: wait_message_range(Alice, 11, 15), 2729: ok 2730: end, 2731: parallel_story(Config, [{alice, 1}], F). 2732: 2733: %% Select first page of recent messages after last known id. 2734: %% Paginating from newest messages to oldest ones. 2735: pagination_last_after_id5(Config) -> 2736: P = ?config(props, Config), 2737: F = fun(Alice) -> 2738: %% Get the last page of size 5 after 5-th message. 2739: RSM = #rsm_in{max=5, direction='before', 2740: after_id=message_id(5, Config)}, 2741: rsm_send(Config, Alice, 2742: stanza_page_archive_request(P, <<"last_after_id5">>, RSM)), 2743: %% wait_message_range(Client, TotalCount, Offset, FromN, ToN), 2744: wait_message_range(Alice, 10, 5, 11, 15), 2745: ok 2746: end, 2747: parallel_story(Config, [{alice, 1}], F). 2748: 2749: %% Select second page of recent messages after last known id. 2750: pagination_last_after_id5_before_id11(Config) -> 2751: P = ?config(props, Config), 2752: F = fun(Alice) -> 2753: RSM = #rsm_in{max=5, direction='before', 2754: after_id=message_id(5, Config), 2755: before_id=message_id(11, Config)}, 2756: rsm_send(Config, Alice, 2757: stanza_page_archive_request(P, <<"last_after_id5_before_id11">>, RSM)), 2758: %% wait_message_range(Client, TotalCount, Offset, FromN, ToN), 2759: wait_message_range(Alice, 5, 0, 6, 10), 2760: ok 2761: end, 2762: parallel_story(Config, [{alice, 1}], F). 2763: 2764: server_returns_item_not_found_for_before_filter_with_nonexistent_id(Config) -> 2765: NonexistentID = <<"AV25E9SCO50K">>, 2766: RSM = #rsm_in{max = 5, direction = 'before', id = NonexistentID}, 2767: StanzaID = <<"before-nonexistent-id">>, 2768: server_returns_item_not_found_for_nonexistent_id(Config, RSM, StanzaID). 2769: 2770: server_returns_item_not_found_for_after_filter_with_nonexistent_id(Config) -> 2771: NonexistentID = <<"AV25E9SCO50K">>, 2772: RSM = #rsm_in{max = 5, direction = 'after', id = NonexistentID}, 2773: StanzaID = <<"after-nonexistent-id">>, 2774: server_returns_item_not_found_for_nonexistent_id(Config, RSM, StanzaID). 2775: 2776: server_returns_item_not_found_for_nonexistent_id(Config, RSM, StanzaID) -> 2777: P = ?config(props, Config), 2778: F = fun(Alice) -> 2779: rsm_send(Config, Alice, 2780: stanza_page_archive_request(P, StanzaID, RSM)), 2781: Res = escalus:wait_for_stanza(Alice), 2782: escalus:assert(is_iq_error, Res), 2783: escalus:assert(is_error, [<<"cancel">>, <<"item-not-found">>], Res), 2784: ok 2785: end, 2786: parallel_story(Config, [{alice, 1}], F). 2787: 2788: 2789: %% Test cases for "complete" attribute 2790: %% Complete attribute is used for pagination, telling when to stop paginating. 2791: %% see complete_flag_cases with the whole list of the cases. 2792: %% ----------------------------------------------- 2793: 2794: %% Get last page with most recent messages 2795: %% rsm_id.id is undefined 2796: %% GIVEN 15 archived messages 2797: %% WHEN direction=before, page_size=5 2798: %% THEN complete=false 2799: before_complete_false_last5(Config) -> 2800: P = ?config(props, Config), 2801: F = fun(Alice) -> 2802: %% Get the last page of size 5. 2803: %% Get messages: 11,12,13,14,15 2804: RSM = #rsm_in{max=5, direction=before}, 2805: rsm_send(Config, Alice, 2806: stanza_page_archive_request(P, <<"last5">>, RSM)), 2807: wait_for_complete_archive_response(P, Alice, <<"false">>) 2808: end, 2809: parallel_story(Config, [{alice, 1}], F). 2810: 2811: %% Gets some page in the midle of the result set 2812: %% GIVEN 15 archived messages 2813: %% WHEN direction=before, rsm_id=10, page_size=5 2814: %% THEN complete=false 2815: before_complete_false_before10(Config) -> 2816: P = ?config(props, Config), 2817: F = fun(Alice) -> 2818: %% Get messages: 5,6,7,8,9 2819: RSM = #rsm_in{max=5, direction=before, id=message_id(10, Config)}, 2820: rsm_send(Config, Alice, 2821: stanza_page_archive_request(P, <<"before10">>, RSM)), 2822: wait_for_complete_archive_response(P, Alice, <<"false">>) 2823: end, 2824: parallel_story(Config, [{alice, 1}], F). 2825: 2826: %% Reaches the end of result set 2827: %% No messages are returned. 2828: %% GIVEN 15 archived messages 2829: %% WHEN direction=before, rsm_id=1, page_size=5 2830: %% THEN complete=true 2831: before_complete_true_before1(Config) -> 2832: P = ?config(props, Config), 2833: F = fun(Alice) -> 2834: %% Get no messages 2835: RSM = #rsm_in{max=5, direction=before, id=message_id(1, Config)}, 2836: rsm_send(Config, Alice, 2837: stanza_page_archive_request(P, <<"before1">>, RSM)), 2838: wait_for_complete_archive_response(P, Alice, <<"true">>) 2839: end, 2840: parallel_story(Config, [{alice, 1}], F). 2841: 2842: %% Reaches the end of result set 2843: %% Less than maximum number of messages are returned. 2844: %% GIVEN 15 archived messages 2845: %% WHEN direction=before, rsm_id=5, page_size=5 2846: %% THEN complete=true 2847: before_complete_true_before5(Config) -> 2848: P = ?config(props, Config), 2849: F = fun(Alice) -> 2850: %% Get messages: 1,2,3,4 2851: RSM = #rsm_in{max=5, direction=before, id=message_id(5, Config)}, 2852: rsm_send(Config, Alice, 2853: stanza_page_archive_request(P, <<"before5">>, RSM)), 2854: wait_for_complete_archive_response(P, Alice, <<"true">>) 2855: end, 2856: parallel_story(Config, [{alice, 1}], F). 2857: 2858: %% Reaches the end of result set 2859: %% A special case when exactly maximum number of messages are returned. 2860: %% GIVEN 15 archived messages 2861: %% WHEN direction=before, rsm_id=6, page_size=5 2862: %% THEN complete=true 2863: before_complete_true_before6(Config) -> 2864: P = ?config(props, Config), 2865: F = fun(Alice) -> 2866: %% Get messages: 1,2,3,4,5 2867: RSM = #rsm_in{max=5, direction=before, id=message_id(6, Config)}, 2868: rsm_send(Config, Alice, 2869: stanza_page_archive_request(P, <<"before6">>, RSM)), 2870: wait_for_complete_archive_response(P, Alice, <<"true">>) 2871: end, 2872: parallel_story(Config, [{alice, 1}], F). 2873: 2874: %% First page is not complete, because max is smaller than archive size. 2875: %% rsm_id.id is undefined 2876: %% GIVEN 15 archived messages 2877: %% WHEN direction=after, rsm_id=6, page_size=5 2878: %% THEN complete=false 2879: after_complete_false_first_page(Config) -> 2880: P = ?config(props, Config), 2881: F = fun(Alice) -> 2882: %% Get messages: 1,2,3,4,5 2883: RSM = #rsm_in{max=5, direction='after'}, 2884: rsm_send(Config, Alice, 2885: stanza_page_archive_request(P, <<"firstpage">>, RSM)), 2886: wait_for_complete_archive_response(P, Alice, <<"false">>) 2887: end, 2888: parallel_story(Config, [{alice, 1}], F). 2889: 2890: %% There are still 8-15 messages to paginate after this request. 2891: %% GIVEN 15 archived messages 2892: %% WHEN direction=after, rsm_id=2, page_size=5 2893: %% THEN complete=false 2894: after_complete_false_after2(Config) -> 2895: P = ?config(props, Config), 2896: F = fun(Alice) -> 2897: %% Get messages: 3,4,5,6,7 2898: RSM = #rsm_in{max=5, direction='after', id=message_id(2, Config)}, 2899: rsm_send(Config, Alice, 2900: stanza_page_archive_request(P, <<"after2">>, RSM)), 2901: wait_for_complete_archive_response(P, Alice, <<"false">>) 2902: end, 2903: parallel_story(Config, [{alice, 1}], F). 2904: 2905: %% There is still one message to paginate after this request. 2906: %% GIVEN 15 archived messages 2907: %% WHEN direction=after, rsm_id=9, page_size=5 2908: %% THEN complete=false 2909: after_complete_false_after9(Config) -> 2910: P = ?config(props, Config), 2911: F = fun(Alice) -> 2912: %% Get messages: 10,11,12,13,14 2913: RSM = #rsm_in{max=5, direction='after', id=message_id(9, Config)}, 2914: rsm_send(Config, Alice, 2915: stanza_page_archive_request(P, <<"after9">>, RSM)), 2916: wait_for_complete_archive_response(P, Alice, <<"false">>) 2917: end, 2918: parallel_story(Config, [{alice, 1}], F). 2919: 2920: %% There are no messages to paginate after this request. 2921: %% Special case, when exactly page_size messages are returned. 2922: %% GIVEN 15 archived messages 2923: %% WHEN direction=after, rsm_id=10, page_size=5 2924: %% THEN complete=true 2925: after_complete_true_after10(Config) -> 2926: P = ?config(props, Config), 2927: F = fun(Alice) -> 2928: %% Get the last page of size 5. 2929: %% Get messages: 11,12,13,14,15 2930: RSM = #rsm_in{max=5, direction='after', id=message_id(10, Config)}, 2931: rsm_send(Config, Alice, 2932: stanza_page_archive_request(P, <<"after10">>, RSM)), 2933: wait_for_complete_archive_response(P, Alice, <<"true">>) 2934: end, 2935: parallel_story(Config, [{alice, 1}], F). 2936: 2937: %% There are no messages to paginate after this request. 2938: %% Less than page_size are returned. 2939: %% GIVEN 15 archived messages 2940: %% WHEN direction=after, rsm_id=10, page_size=5 2941: %% THEN complete=true 2942: after_complete_true_after11(Config) -> 2943: P = ?config(props, Config), 2944: F = fun(Alice) -> 2945: %% Get the last page of size 5. 2946: %% Get messages: 12,13,14,15 2947: RSM = #rsm_in{max=5, direction='after', id=message_id(11, Config)}, 2948: rsm_send(Config, Alice, 2949: stanza_page_archive_request(P, <<"after11">>, RSM)), 2950: wait_for_complete_archive_response(P, Alice, <<"true">>) 2951: end, 2952: parallel_story(Config, [{alice, 1}], F). 2953: 2954: 2955: %% Test cases for preferences IQs 2956: %% ------------------------------------------------------------------ 2957: 2958: prefs_set_request(Config) -> 2959: _P = ?config(props, Config), 2960: F = fun(Alice) -> 2961: %% Send 2962: %% 2963: %% <iq type='set' id='juliet2'> 2964: %% <prefs xmlns='urn:xmpp:mam:tmp' default="roster"> 2965: %% <always> 2966: %% <jid>romeo@montague.net</jid> 2967: %% </always> 2968: %% <never> 2969: %% <jid>montague@montague.net</jid> 2970: %% </never> 2971: %% </prefs> 2972: %% </iq> 2973: escalus:send(Alice, stanza_prefs_set_request(<<"roster">>, 2974: [<<"romeo@montague.net">>], 2975: [<<"montague@montague.net">>], 2976: mam_ns_binary())), 2977: ReplySet = escalus:wait_for_stanza(Alice), 2978: 2979: escalus:send(Alice, stanza_prefs_get_request(mam_ns_binary())), 2980: ReplyGet = escalus:wait_for_stanza(Alice), 2981: 2982: ResultIQ1 = parse_prefs_result_iq(ReplySet), 2983: ResultIQ2 = parse_prefs_result_iq(ReplyGet), 2984: ?assert_equal(ResultIQ1, ResultIQ2), 2985: ok 2986: end, 2987: escalus:story(Config, [{alice, 1}], F). 2988: 2989: query_get_request(Config) -> 2990: F = fun(Alice) -> 2991: QueryXmlns = mam_ns_binary_v04(), 2992: escalus:send(Alice, stanza_query_get_request(QueryXmlns)), 2993: ReplyFields = escalus:wait_for_stanza(Alice), 2994: ResponseXmlns = exml_query:path(ReplyFields, 2995: [{element, <<"query">>}, 2996: {element, <<"x">>}, 2997: {element, <<"field">>}, 2998: {element, <<"value">>}, 2999: cdata]), 3000: ?assert_equal(QueryXmlns, ResponseXmlns) 3001: end, 3002: escalus_fresh:story(Config, [{alice, 1}], F). 3003: 3004: %% Test reproducing https://github.com/esl/MongooseIM/issues/263 3005: %% The idea is this: in a "perfect" world jid elements are put together 3006: %% without whitespaces. In the real world it is not true. 3007: %% Put "\n" between two jid elements. 3008: prefs_set_cdata_request(Config) -> 3009: _P = ?config(props, Config), 3010: F = fun(Alice) -> 3011: %% Send 3012: %% 3013: %% <iq type='set' id='juliet2'> 3014: %% <prefs xmlns='urn:xmpp:mam:tmp' default="roster"> 3015: %% <always> 3016: %% <jid>romeo@montague.net</jid> 3017: %% <jid>montague@montague.net</jid> 3018: %% </always> 3019: %% </prefs> 3020: %% </iq> 3021: escalus:send(Alice, stanza_prefs_set_request(<<"roster">>, 3022: [<<"romeo@montague.net">>, 3023: {xmlcdata, <<"\n">>}, %% Put as it is 3024: <<"montague@montague.net">>], [], 3025: mam_ns_binary_v04())), 3026: ReplySet = escalus:wait_for_stanza(Alice), 3027: 3028: escalus:send(Alice, stanza_prefs_get_request(mam_ns_binary_v04())), 3029: ReplyGet = escalus:wait_for_stanza(Alice), 3030: 3031: ResultIQ1 = parse_prefs_result_iq(ReplySet), 3032: ResultIQ2 = parse_prefs_result_iq(ReplyGet), 3033: ?assert_equal(ResultIQ1, ResultIQ2), 3034: ok 3035: end, 3036: escalus_fresh:story(Config, [{alice, 1}], F). 3037: 3038: mam_service_discovery(Config) -> 3039: _P = ?config(props, Config), 3040: F = fun(Alice) -> 3041: Server = escalus_client:server(Alice), 3042: discover_features(Config, Alice, Server) 3043: end, 3044: escalus_fresh:story(Config, [{alice, 1}], F). 3045: 3046: %% Check, that MUC is supported. 3047: muc_service_discovery(Config) -> 3048: _P = ?config(props, Config), 3049: F = fun(Alice) -> 3050: Domain = domain(), 3051: Server = escalus_client:server(Alice), 3052: escalus:send(Alice, escalus_stanza:service_discovery(Server)), 3053: Stanza = escalus:wait_for_stanza(Alice), 3054: escalus:assert(has_service, [muc_host()], Stanza), 3055: escalus:assert(is_stanza_from, [Domain], Stanza), 3056: 3057: discover_features(Config, Alice, muc_host()) 3058: end, 3059: escalus:fresh_story(Config, [{alice, 1}], F). 3060: 3061: discover_features(Config, Client, Service) -> 3062: escalus:send(Client, escalus_stanza:disco_info(Service)), 3063: Stanza = escalus:wait_for_stanza(Client), 3064: escalus:assert(is_iq_result, Stanza), 3065: escalus:assert(has_feature, [mam_ns_binary_v04()], Stanza), 3066: escalus:assert(has_feature, [mam_ns_binary_v06()], Stanza), 3067: escalus:assert(has_feature, [retract_ns()], Stanza), 3068: ?assert_equal(message_retraction_is_enabled(Config), 3069: escalus_pred:has_feature(retract_tombstone_ns(), Stanza)). 3070: 3071: metric_incremented_on_archive_request(ConfigIn) -> 3072: P = ?config(props, ConfigIn), 3073: F = fun(Alice) -> 3074: escalus:send(Alice, stanza_archive_request(P, <<"metric_q1">>)), 3075: Res = wait_archive_respond(Alice), 3076: assert_respond_size(0, Res), 3077: assert_respond_query_id(P, <<"metric_q1">>, parse_result_iq(Res)), 3078: ok 3079: end, 3080: HostType = domain_helper:host_type(mim), 3081: HostTypePrefix = domain_helper:make_metrics_prefix(HostType), 3082: MongooseMetrics = [{[HostTypePrefix, backends, mod_mam, lookup], changed}], 3083: Config = [{mongoose_metrics, MongooseMetrics} | ConfigIn], 3084: escalus_fresh:story(Config, [{alice, 1}], F). 3085: 3086: metrics_incremented_for_async_pools(Config) -> 3087: OldValue = get_mongoose_async_metrics(), 3088: archived(Config), 3089: Validator = fun(NewValue) -> OldValue =/= NewValue end, 3090: mongoose_helper:wait_until( 3091: fun get_mongoose_async_metrics/0, 3092: true, #{validator => Validator, name => ?FUNCTION_NAME}). 3093: 3094: get_mongoose_async_metrics() -> 3095: HostType = domain_helper:host_type(mim), 3096: HostTypePrefix = domain_helper:make_metrics_prefix(HostType), 3097: #{batch_flushes => get_mongoose_async_metrics(HostTypePrefix, batch_flushes), 3098: timed_flushes => get_mongoose_async_metrics(HostTypePrefix, timed_flushes)}. 3099: 3100: get_mongoose_async_metrics(HostTypePrefix, MetricName) -> 3101: Metric = [HostTypePrefix, mongoose_async_pools, pm_mam, MetricName], 3102: {ok, Value} = rpc(mim(), mongoose_metrics, get_metric_value, [Metric]), 3103: {value, Count} = lists:keyfind(value, 1, Value), 3104: Count. 3105: 3106: metric_incremented_when_store_message(Config) -> 3107: archived(Config). 3108: 3109: messages_filtered_when_prefs_default_policy_is_always(Config) -> 3110: run_prefs_cases(always, Config). 3111: 3112: messages_filtered_when_prefs_default_policy_is_never(Config) -> 3113: run_prefs_cases(never, Config). 3114: 3115: messages_filtered_when_prefs_default_policy_is_roster(Config) -> 3116: run_prefs_cases(roster, Config). 3117: 3118: 3119: -spec enter_room(Config :: proplists:proplist(), [User :: term()]) -> 3120: {Room :: binary(), RoomAddr :: binary()}. 3121: enter_room(Config, Users) -> 3122: Room = ?config(room, Config), 3123: RoomAddr = room_address(Room), 3124: [escalus:send(User, stanza_muc_enter_room(Room, nick(User))) || User <- Users], 3125: {Room, RoomAddr}. 3126: 3127: %% First write all messages, than read and check 3128: run_prefs_cases(DefaultPolicy, ConfigIn) -> 3129: P = ?config(props, ConfigIn), 3130: F = fun(Config, Alice, Bob, Kate) -> 3131: make_alice_and_bob_friends(Alice, Bob), 3132: %% Just send messages for each prefs configuration 3133: Namespace = mam_ns_binary_v04(), 3134: Funs = [run_prefs_case(Case, Namespace, Alice, Bob, Kate, Config) 3135: || Case <- prefs_cases2(), 3136: default_policy(Case) =:= DefaultPolicy], 3137: 3138: maybe_wait_for_archive(Config), 3139: 3140: %% Get ALL messages using several queries if required 3141: Stanzas = get_all_messages(P, Alice), 3142: ParsedMessages = parse_messages(Stanzas), 3143: Bodies = [B || #forwarded_message{message_body=B} <- ParsedMessages], 3144: 3145: %% Check messages, print out all failed cases 3146: Fails = lists:append([Fun(Bodies) || Fun <- Funs]), 3147: %% If fails consult with ct:pal/2 why 3148: ?assert_equal([], Fails) 3149: end, 3150: escalus_fresh:story_with_config(ConfigIn, [{alice, 1}, {bob, 1}, {kate, 1}], F). 3151: 3152: %% The same as prefs_set_request case but for different configurations 3153: run_set_and_get_prefs_cases(ConfigIn) -> 3154: _P = ?config(props, ConfigIn), 3155: F = fun(Config, Alice, _Bob, _Kate) -> 3156: Namespace = mam_ns_binary_v04(), 3157: [run_set_and_get_prefs_case(Case, Namespace, Alice, Config) || Case <- prefs_cases2()] 3158: end, 3159: escalus_fresh:story_with_config(ConfigIn, [{alice, 1}, {bob, 1}, {kate, 1}], F). 3160: 3161: %% MAM's implementation specific test 3162: check_user_exist(Config) -> 3163: %% when 3164: [{_, AdminSpec}] = escalus_users:get_users([admin]), 3165: [AdminU, AdminS, AdminP] = escalus_users:get_usp(Config, AdminSpec), 3166: JID = mongoose_helper:make_jid(AdminU, AdminS), 3167: ok = rpc(mim(), ejabberd_auth, try_register, [JID, AdminP]), 3168: %% admin user already registered 3169: {ok, HostType} = rpc(mim(), mongoose_domain_core, get_host_type, [AdminS]), 3170: true = rpc(mim(), ejabberd_auth, does_user_exist, 3171: [HostType, JID, stored]), 3172: false = rpc(mim(), ejabberd_auth, does_user_exist, 3173: [HostType, mongoose_helper:make_jid(<<"fake-user">>, AdminS), stored]), 3174: false = rpc(mim(), ejabberd_auth, does_user_exist, 3175: [HostType, mongoose_helper:make_jid(AdminU, <<"fake-domain">>), stored]), 3176: %% cleanup 3177: ok = rpc(mim(), ejabberd_auth, remove_user, [JID]). 3178: 3179: %% This function supports only one device, one user. 3180: %% We don't send initial presence to avoid presence broadcasts between resources 3181: %% of the same user from different stories. 3182: %% It is limited comparing to escalus story, but reduces CPU usage, because we don't 3183: %% need to send any presences. 3184: parallel_story(Config, [{_, 1}] = ResourceCounts, F) -> 3185: Config1 = override_for_parallel(Config), 3186: escalus:story(Config1, ResourceCounts, F). 3187: 3188: override_for_parallel(Config) -> 3189: Overrides = [ 3190: {start_ready_clients, start_ready_clients()} 3191: ], 3192: [{escalus_overrides, Overrides} | Config]. 3193: 3194: start_ready_clients() -> 3195: fun(Config, [{UserSpec, BaseResource}]) -> 3196: Suffix = list_to_binary(pid_to_list(self()) -- "<>"), 3197: Resource = <<BaseResource/binary, Suffix/binary>>, 3198: {ok, Client} = escalus_client:start(Config, UserSpec, Resource), 3199: [Client] 3200: end. 3201: 3202: text_search_messages() -> 3203: [ 3204: <<"Tongue chicken jowl hamburger duis exercitation.">>, 3205: <<"Ribs eu aliquip pork veniam dolor jowl id laborum in frankfurter culpa ribs.">>, 3206: <<"Fatback ut labore pariatur, eiusmod esse dolore turducken jowl exercitation ", 3207: "shankle shoulder.">>, 3208: <<"Kevin ribeye short ribs, nostrud short loin quis voluptate cow. Do brisket eu ", 3209: "sunt tail ullamco cow in bacon burgdoggen.">>, 3210: <<"Occaecat in voluptate incididunt aliqua dolor bacon salami anim picanha pork ", 3211: "reprehenderit pancetta tail.">>, 3212: <<"Nisi shank doner dolore officia ribeye. Proident shankle tenderloin consequat ", 3213: "bresaola quis tongue ut sirloin pork chop pariatur fatback ex cupidatat venison.">>, 3214: <<"Brisket in pastrami dolore cupidatat. Est corned beef ad ribeye ball tip aliqua ", 3215: "cupidatat andouille cillum et consequat leberkas.">>, 3216: <<"Qui mollit short ribs, capicola bresaola pork meatloaf kielbasa und culpa.">>, 3217: <<"Meatloaf esse jowl do ham hock consequat. Duis laboris ribeye ullamco, sed elit ", 3218: "porchetta sirloin.">>, 3219: <<"In boudin ad et salami exercitation sausage flank strip steak ball tip dolore ", 3220: "pig officia.">>, 3221: <<"Spare ribs landjaeger pork belly, chuck aliquip turducken beef culpa nostrud.">> 3222: ]. 3223: 3224: %% --------- MUC Light stories helpers ---------- 3225: 3226: when_pm_message_is_sent(Sender, Receiver, Body) -> 3227: escalus:send(Sender, escalus_stanza:chat_to(Receiver, Body)). 3228: 3229: then_pm_message_is_received(Receiver, Body) -> 3230: escalus:assert(is_chat_message, [Body], escalus:wait_for_stanza(Receiver)). 3231: 3232: %% Message retraction helpers 3233: 3234: check_archive_after_retraction(Config, Client, ApplyToElement, Body) -> 3235: case message_should_be_retracted(Config) of 3236: true -> expect_tombstone_and_retraction_message(Client, ApplyToElement); 3237: false -> expect_original_and_retraction_message(Client, ApplyToElement, Body) 3238: end. 3239: 3240: message_should_be_retracted(Config) -> 3241: message_retraction_is_enabled(Config) andalso 3242: (retract_on_stanza_id(Config) orelse origin_id_to_retract(Config) =:= origin_id()). 3243: 3244: retract_on_stanza_id(Config) -> 3245: stanza_id =:= ?config(retract_on, Config). 3246: 3247: message_retraction_is_enabled(Config) -> 3248: BasicGroup = ?config(basic_group, Config), 3249: BasicGroup =/= disabled_retraction andalso BasicGroup =/= muc_disabled_retraction. 3250: 3251: expect_tombstone_and_retraction_message(Client, ApplyToElement) -> 3252: [ArcMsg1, ArcMsg2] = respond_messages(assert_respond_size(2, wait_archive_respond(Client))), 3253: #forwarded_message{message_body = undefined, 3254: message_children = [#xmlel{name = <<"retracted">>}]} = parse_forwarded_message(ArcMsg1), 3255: #forwarded_message{message_body = undefined, 3256: message_children = [ApplyToElement]} = parse_forwarded_message(ArcMsg2). 3257: 3258: expect_original_and_retraction_message(Client, ApplyToElement, Body) -> 3259: [ArcMsg1, ArcMsg2] = respond_messages(assert_respond_size(2, wait_archive_respond(Client))), 3260: #forwarded_message{message_body = Body} = parse_forwarded_message(ArcMsg1), 3261: #forwarded_message{message_body = undefined, 3262: message_children = [ApplyToElement]} = parse_forwarded_message(ArcMsg2). 3263: 3264: retraction_message(Type, To, ApplyToElement) -> 3265: #xmlel{name = <<"message">>, 3266: attrs = [{<<"type">>, Type}, 3267: {<<"to">>, To}], 3268: children = [ApplyToElement]}. 3269: 3270: origin_id_element(OriginId) -> 3271: #xmlel{name = <<"origin-id">>, 3272: attrs = [{<<"xmlns">>, <<"urn:xmpp:sid:0">>}, 3273: {<<"id">>, OriginId}]}. 3274: 3275: apply_to_element(Config, Copy) -> 3276: {RetractOn, Id} = case ?config(retract_on, Config) of 3277: stanza_id -> {stanza_id, stanza_id_from_msg(Copy)}; 3278: _ -> {origin_id, origin_id_to_retract(Config)} 3279: end, 3280: #xmlel{name = <<"apply-to">>, 3281: attrs = [{<<"id">>, Id}, 3282: {<<"xmlns">>, <<"urn:xmpp:fasten:0">>}], 3283: children = [retract_element(RetractOn)] 3284: }. 3285: 3286: stanza_id_from_msg(Msg) -> 3287: case exml_query:path(Msg, [{element, <<"stanza-id">>}, {attr, <<"id">>}]) of 3288: undefined -> exml_query:path(Msg, [{element, <<"result">>}, {attr, <<"id">>}]); 3289: Id -> Id 3290: end. 3291: 3292: retract_element(origin_id) -> 3293: #xmlel{name = <<"retract">>, 3294: attrs = [{<<"xmlns">>, <<"urn:xmpp:message-retract:0">>}]}; 3295: retract_element(stanza_id) -> 3296: #xmlel{name = <<"retract">>, 3297: attrs = [{<<"xmlns">>, <<"urn:esl:message-retract-by-stanza-id:0">>}]}. 3298: 3299: origin_id_to_retract(Config) -> 3300: proplists:get_value(origin_id, Config, origin_id()). 3301: 3302: origin_id() -> 3303: <<"orig-id-1">>.