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