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