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">>.