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