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