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