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