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