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