1: -module(inbox_SUITE).
    2: -compile([export_all, nowarn_export_all]).
    3: 
    4: -include_lib("common_test/include/ct.hrl").
    5: -include_lib("eunit/include/eunit.hrl").
    6: -include_lib("escalus/include/escalus_xmlns.hrl").
    7: -include_lib("exml/include/exml.hrl").
    8: -include_lib("jid/include/jid.hrl").
    9: -include_lib("inbox.hrl").
   10: 
   11: %% tests
   12: -import(muc_light_helper, [room_bin_jid/1]).
   13: -import(inbox_helper, [
   14:                        inbox_modules/0,
   15:                        muclight_modules/0,
   16:                        inbox_opts/0,
   17:                        muc_domain/0,
   18:                        parse_form_iq/1,
   19:                        check_inbox/2, check_inbox/3,
   20:                        check_inbox/4,
   21:                        clear_inbox_all/0,
   22:                        given_conversations_between/2,
   23:                        assert_invalid_inbox_form_value_error/3,
   24:                        assert_invalid_reset_inbox/4,
   25:                        extract_user_specs/1
   26:                       ]).
   27: 
   28: -define(ROOM3, <<"testroom3">>).
   29: -define(ROOM4, <<"testroom4">>).
   30: 
   31: %%--------------------------------------------------------------------
   32: %% Suite configuration
   33: %%--------------------------------------------------------------------
   34: 
   35: all() ->
   36:     inbox_helper:skip_or_run_inbox_tests(tests()).
   37: 
   38: tests() ->
   39:     [
   40:      {group, regular},
   41:      {group, async_pools}
   42:     ].
   43: 
   44: groups() ->
   45:     Gs = [
   46:      {generic, [],
   47:       [
   48:        disco_service,
   49:        returns_valid_form,
   50:        returns_error_when_first_bad_form_field_encountered,
   51:        returns_error_when_bad_form_field_start_sent,
   52:        returns_error_when_bad_form_field_end_sent,
   53:        returns_error_when_bad_form_field_order_sent,
   54:        returns_error_when_bad_form_field_hidden_read_sent,
   55:        returns_error_when_bad_reset_field_jid,
   56:        returns_error_when_no_reset_field_jid,
   57:        returns_error_when_unknown_field_sent
   58:       ]},
   59:      {limit_result, [],
   60:       [
   61:        inbox_result_cannot_exceed_configured_limit,
   62:        should_apply_lower_limit_when_both_provided
   63:       ]},
   64:      {one_to_one, [],
   65:       [
   66:        user_has_empty_inbox,
   67:        msg_sent_stored_in_inbox,
   68:        msg_sent_stored_in_inbox_iq_id_as_queryid_fallback,
   69:        msg_sent_stored_in_inbox_queryid,
   70:        msg_with_no_store_is_not_stored_in_inbox,
   71:        msg_with_store_hint_is_always_stored,
   72:        carbons_are_not_stored,
   73:        user_has_two_conversations,
   74:        msg_sent_to_offline_user,
   75:        msg_sent_to_not_existing_user,
   76:        user_has_two_unread_messages,
   77:        other_resources_do_not_interfere,
   78:        reset_unread_counter_with_reset_chat_marker,
   79:        reset_unread_counter_with_reset_stanza,
   80:        try_to_reset_unread_counter_with_bad_marker,
   81:        non_reset_marker_should_not_affect_inbox,
   82:        user_has_only_unread_messages_or_only_read,
   83:        reset_unread_counter_and_show_only_unread,
   84:        check_total_unread_count_and_active_conv_count,
   85:        check_total_unread_count_when_there_are_no_active_conversations,
   86:        total_unread_count_and_active_convs_are_zero_at_no_activity
   87:       ]},
   88:      {muclight, [],
   89:       [
   90:        simple_groupchat_stored_in_all_inbox,
   91:        advanced_groupchat_stored_in_all_inbox,
   92:        groupchat_markers_one_reset,
   93:        non_reset_marker_should_not_affect_muclight_inbox,
   94:        groupchat_reset_stanza_resets_inbox,
   95:        create_groupchat,
   96:        leave_and_remove_conversation,
   97:        groupchat_markers_one_reset_room_created,
   98:        groupchat_markers_all_reset_room_created,
   99:        inbox_does_not_trigger_does_user_exist
  100:       ]},
  101:      {muclight_config, [sequence],
  102:       [
  103:        create_groupchat_no_affiliation_stored,
  104:        leave_and_store_conversation,
  105:        no_aff_stored_and_remove_on_kicked,
  106:        no_stored_and_remain_after_kicked,
  107:        system_message_is_correctly_avoided
  108:       ]},
  109:      {muc, [],
  110:       [
  111:        simple_groupchat_stored_in_all_inbox_muc,
  112:        simple_groupchat_stored_in_offline_users_inbox_muc,
  113:        unread_count_is_the_same_after_going_online_again,
  114:        unread_count_is_reset_after_sending_chatmarker,
  115:        non_reset_marker_should_not_affect_muc_inbox,
  116:        unread_count_is_reset_after_sending_reset_stanza,
  117:        private_messages_are_handled_as_one2one
  118:       ]},
  119:      {timestamps, [],
  120:       [
  121:        timestamp_is_updated_on_new_message,
  122:        order_by_timestamp_ascending,
  123:        get_by_timestamp_range,
  124:        get_with_start_timestamp,
  125:        get_with_end_timestamp
  126:       ]},
  127:      {bin, [],
  128:       [
  129:        timeout_cleaner_flush_all,
  130:        rest_api_bin_flush_all,
  131:        rest_api_bin_flush_all_errors,
  132:        rest_api_bin_flush_user,
  133:        rest_api_bin_flush_user_errors,
  134:        xmpp_bin_flush,
  135:        bin_is_not_included_by_default
  136:       ]},
  137:      {regular, [], test_groups()},
  138:      {async_pools, [], [{group, bin} | test_groups()]}
  139:     ],
  140:     inbox_helper:maybe_run_in_parallel(Gs).
  141: 
  142: test_groups() ->
  143:     [
  144:      {group, generic},
  145:      {group, limit_result},
  146:      {group, one_to_one},
  147:      {group, muclight},
  148:      {group, muclight_config},
  149:      {group, muc},
  150:      {group, timestamps}
  151:     ].
  152: 
  153: suite() ->
  154:     escalus:suite().
  155: 
  156: %%--------------------------------------------------------------------
  157: %% Init & teardown
  158: %%--------------------------------------------------------------------
  159: 
  160: init_per_suite(Config) ->
  161:     mongoose_helper:inject_module(?MODULE),
  162:     escalus:init_per_suite(Config).
  163: 
  164: end_per_suite(Config) ->
  165:     escalus:end_per_suite(Config).
  166: 
  167: init_per_group(GroupName, Config) when GroupName =:= regular; GroupName =:= async_pools ->
  168:     HostType = domain_helper:host_type(),
  169:     SecHostType = domain_helper:secondary_host_type(),
  170:     Config1 = dynamic_modules:save_modules(HostType, Config),
  171:     Config2 = dynamic_modules:save_modules(SecHostType, Config1),
  172:     InboxOptions = inbox_helper:inbox_opts(GroupName),
  173:     Backend = mongoose_helper:mnesia_or_rdbms_backend(),
  174:     ModOffline = config_parser_helper:mod_config(mod_offline, #{backend => Backend}),
  175:     ok = dynamic_modules:ensure_modules(HostType,
  176:            inbox_helper:inbox_modules(GroupName)
  177:            ++ inbox_helper:muclight_modules()
  178:            ++ [{mod_offline, ModOffline}]),
  179:     ok = dynamic_modules:ensure_modules(SecHostType,
  180:            [{mod_inbox, InboxOptions#{aff_changes := false}}]),
  181:     [{inbox_opts, InboxOptions} | Config2];
  182: 
  183: init_per_group(muclight, Config) ->
  184:     ok = dynamic_modules:ensure_modules(domain_helper:host_type(), muclight_modules()),
  185:     inbox_helper:reload_inbox_option(Config, groupchat, [muclight]);
  186: init_per_group(muclight_config, Config) ->
  187:     ok = dynamic_modules:ensure_modules(domain_helper:host_type(), muclight_modules()),
  188:     Config1 = inbox_helper:reload_inbox_option(Config, groupchat, [muclight]),
  189:     escalus:create_users(Config1, escalus:get_users([alice, alice_bis, bob, kate, mike]));
  190: init_per_group(muc, Config) ->
  191:     muc_helper:load_muc(),
  192:     inbox_helper:reload_inbox_option(Config, groupchat, [muc]);
  193: init_per_group(limit_result, Config) ->
  194:     OptKey = [{modules, domain_helper:host_type()}, mod_inbox, max_result_limit],
  195:     mongoose_helper:backup_and_set_config_option(Config, OptKey, 2);
  196: init_per_group(_GroupName, Config) ->
  197:     Config.
  198: 
  199: end_per_group(GroupName, Config) when GroupName =:= regular; GroupName =:= async_pools ->
  200:     muc_light_helper:clear_db(domain_helper:host_type()),
  201:     escalus_fresh:clean(),
  202:     dynamic_modules:restore_modules(Config);
  203: end_per_group(muclight_config, Config) ->
  204:     escalus:delete_users(Config, escalus:get_users([alice, alice_bis, bob, kate, mike]));
  205: end_per_group(muc, Config) ->
  206:     inbox_helper:restore_inbox_option(Config),
  207:     muc_helper:unload_muc();
  208: end_per_group(limit_result, Config) ->
  209:     mongoose_helper:restore_config(Config),
  210:     Config;
  211: end_per_group(_GroupName, Config) ->
  212:     Config.
  213: 
  214: init_per_testcase(timeout_cleaner_flush_all, Config) ->
  215:     clear_inbox_all(),
  216:     inbox_helper:reload_inbox_option(Config, [{bin_ttl, 0}, {bin_clean_after, 10}]),
  217:     escalus:init_per_testcase(timeout_cleaner_flush_all, Config);
  218: init_per_testcase(TS, Config)
  219:   when TS =:= create_groupchat_no_affiliation_stored;
  220:        TS =:= system_message_is_correctly_avoided ->
  221:     clear_inbox_all(),
  222:     inbox_helper:reload_inbox_option(Config, aff_changes, false),
  223:     escalus:init_per_testcase(TS, Config);
  224: init_per_testcase(leave_and_store_conversation, Config) ->
  225:     clear_inbox_all(),
  226:     inbox_helper:reload_inbox_option(Config, remove_on_kicked, false),
  227:     escalus:init_per_testcase(leave_and_store_conversation, Config);
  228: init_per_testcase(no_aff_stored_and_remove_on_kicked, Config) ->
  229:     clear_inbox_all(),
  230:     muc_light_helper:create_room(?ROOM3, muc_light_helper:muc_host(), alice, [bob, kate],
  231:                                  Config, muc_light_helper:ver(1)),
  232:     inbox_helper:reload_inbox_option(Config, [{remove_on_kicked, true}, {aff_changes, false}]),
  233:     escalus:init_per_testcase(no_aff_stored_and_remove_on_kicked, Config);
  234: init_per_testcase(no_stored_and_remain_after_kicked, Config) ->
  235:     clear_inbox_all(),
  236:     muc_light_helper:create_room(?ROOM4, muc_light_helper:muc_host(), alice, [bob, kate],
  237:                                  Config, muc_light_helper:ver(1)),
  238:     inbox_helper:reload_inbox_option(Config, [{remove_on_kicked, false}, {aff_changes, true}]),
  239:     escalus:init_per_testcase(no_stored_and_remain_after_kicked, Config);
  240: init_per_testcase(CaseName, Config) ->
  241:     escalus:init_per_testcase(CaseName, Config).
  242: 
  243: end_per_testcase(timeout_cleaner_flush_all, Config) ->
  244:     inbox_helper:restore_inbox_option(Config),
  245:     escalus:end_per_testcase(timeout_cleaner_flush_all, Config);
  246: end_per_testcase(TS, Config)
  247:   when TS =:= create_groupchat_no_affiliation_stored;
  248:        TS =:= system_message_is_correctly_avoided ->
  249:     clear_inbox_all(),
  250:     inbox_helper:restore_inbox_option(Config),
  251:     escalus:end_per_testcase(TS, Config);
  252: end_per_testcase(leave_and_store_conversation, Config) ->
  253:     clear_inbox_all(),
  254:     inbox_helper:restore_inbox_option(Config),
  255:     escalus:end_per_testcase(leave_and_store_conversation, Config);
  256: end_per_testcase(no_aff_stored_and_remove_on_kicked, Config) ->
  257:     clear_inbox_all(),
  258:     inbox_helper:restore_inbox_option(Config),
  259:     escalus:end_per_testcase(no_aff_stored_and_remove_on_kicked, Config);
  260: end_per_testcase(no_stored_and_remain_after_kicked, Config) ->
  261:     clear_inbox_all(),
  262:     inbox_helper:restore_inbox_option(Config),
  263:     escalus:end_per_testcase(no_stored_and_remain_after_kicked, Config);
  264: end_per_testcase(msg_sent_to_not_existing_user, Config) ->
  265:     HostType = domain_helper:host_type(),
  266:     escalus_ejabberd:rpc(mod_inbox_utils, clear_inbox, [HostType, <<"not_existing_user">>,<<"localhost">>]),
  267:     escalus:end_per_testcase(msg_sent_to_not_existing_user, Config);
  268: end_per_testcase(CaseName, Config) ->
  269:     escalus:end_per_testcase(CaseName, Config).
  270: 
  271: 
  272: %%--------------------------------------------------------------------
  273: %% Generic Inbox tests
  274: %%--------------------------------------------------------------------
  275: 
  276: disco_service(Config) ->
  277:     escalus:fresh_story(Config, [{alice, 1}], fun(Alice) ->
  278:             Server = escalus_client:server(Alice),
  279:             escalus:send(
  280:               Alice, escalus_stanza:to(escalus_stanza:iq_get(?NS_DISCO_INFO, []), Server)),
  281:             Stanza = escalus:wait_for_stanza(Alice),
  282:             Features = exml_query:paths(Stanza, [{element, <<"query">>},
  283:                                                  {element, <<"feature">>},
  284:                                                  {attr, <<"var">>}]),
  285:             ?assertEqual(true, lists:member(inbox_helper:inbox_ns(), Features))
  286:         end).
  287: 
  288: returns_valid_form(Config) ->
  289:     escalus:fresh_story(Config, [{alice, 1}], fun(Alice) ->
  290:         escalus:send(Alice, inbox_helper:get_inbox_form_stanza()),
  291:         ResIQ = escalus:wait_for_stanza(Alice),
  292:         InboxNS = inbox_helper:inbox_ns(),
  293:         #{ field_count := 7 } = Form = parse_form_iq(ResIQ),
  294:         #{ <<"FORM_TYPE">> := #{ type := <<"hidden">>,
  295:                                  value := InboxNS } } = Form,
  296:         #{ <<"start">> := #{ type := <<"text-single">> } } = Form,
  297:         #{ <<"end">> := #{ type := <<"text-single">> } } = Form,
  298:         #{ <<"archive">> := #{ type := <<"boolean">>,
  299:                                value := <<"false">> } } = Form,
  300:         #{ <<"box">> := #{ type := <<"list-single">>,
  301:                            value := <<"all">>,
  302:                            options := BoxOptions } } = Form,
  303:         #{ <<"order">> := #{ type := <<"list-single">>,
  304:                              value := <<"desc">>,
  305:                              options := OrderOptions } } = Form,
  306:         [<<"asc">>, <<"desc">>] = lists:sort(OrderOptions),
  307:         [<<"all">>, <<"archive">>, <<"bin">>, <<"inbox">>, <<"other">>] = lists:sort(BoxOptions),
  308:         #{ <<"hidden_read">> := #{ type := <<"text-single">> } } = Form
  309:       end).
  310: 
  311: returns_error_when_bad_form_field_order_sent(Config) ->
  312:     escalus:fresh_story(Config, [{alice, 1}], fun(Alice) ->
  313:         assert_invalid_inbox_form_value_error(Alice, <<"order">>, <<"invalid">>)
  314:       end).
  315: 
  316: returns_error_when_bad_form_field_start_sent(Config) ->
  317:     escalus:fresh_story(Config, [{alice, 1}], fun(Alice) ->
  318:         assert_invalid_inbox_form_value_error(Alice, <<"start">>, <<"invalid">>)
  319:       end).
  320: 
  321: returns_error_when_bad_form_field_end_sent(Config) ->
  322:     escalus:fresh_story(Config, [{alice, 1}], fun(Alice) ->
  323:       assert_invalid_inbox_form_value_error(Alice, <<"end">>, <<"invalid">>)
  324:     end).
  325: 
  326: returns_error_when_bad_form_field_hidden_read_sent(Config) ->
  327:     escalus:fresh_story(Config, [{alice, 1}], fun(Alice) ->
  328:       assert_invalid_inbox_form_value_error(Alice, <<"hidden_read">>, <<"invalid">>)
  329:     end).
  330: 
  331: returns_error_when_bad_reset_field_jid(Config) ->
  332:     escalus:fresh_story(Config, [{alice, 1}], fun(Alice) ->
  333:       assert_invalid_reset_inbox(
  334:         Alice, <<"$@/">>, <<"jid">>, <<"invalid-jid">>)
  335:     end).
  336: 
  337: returns_error_when_no_reset_field_jid(Config) ->
  338:     escalus:fresh_story(Config, [{alice, 1}], fun(Alice) ->
  339:       assert_invalid_reset_inbox(
  340:         Alice, undefined, <<"jid">>, <<"jid-required">>)
  341:     end).
  342: 
  343: 
  344: returns_error_when_first_bad_form_field_encountered(Config) ->
  345:     escalus:fresh_story(Config, [{alice, 1}], fun(Alice) ->
  346:         Stanza = inbox_helper:make_inbox_stanza(#{<<"start">> => <<"invalid">>,
  347:                                                   <<"end">> => <<"invalid">>}, false),
  348:         escalus:send(Alice, Stanza),
  349:         [ResIQ] = escalus:wait_for_stanzas(Alice, 1),
  350:         escalus_pred:is_iq_error(ResIQ),
  351:         ErrorMsg = inbox_helper:get_error_message(ResIQ),
  352:         inbox_helper:assert_message_content(ErrorMsg, <<"field=end">>, <<"value=invalid">>)
  353:       end).
  354: 
  355: returns_error_when_unknown_field_sent(Config) ->
  356:     escalus:fresh_story(Config, [{alice, 1}], fun(Alice) ->
  357:         Stanza = inbox_helper:make_inbox_stanza(#{<<"unknown_field">> => <<"unknown_field_value">>}, false),
  358:         escalus:send(Alice, Stanza),
  359:         [ResIQ] = escalus:wait_for_stanzas(Alice, 1),
  360:         escalus_pred:is_iq_error(ResIQ),
  361:         ErrorMsg = inbox_helper:get_error_message(ResIQ),
  362:         inbox_helper:assert_message_content(ErrorMsg, <<"field=unknown_field">>, <<"value=unknown_field_value">>)
  363:       end).
  364: 
  365: 
  366: %%--------------------------------------------------------------------
  367: %% Inbox tests one-to-one
  368: %%--------------------------------------------------------------------
  369: 
  370: user_has_empty_inbox(Config) ->
  371:     escalus:fresh_story(Config, [{kate, 1}], fun(Kate) ->
  372:         Stanza = inbox_helper:make_inbox_stanza(),
  373:         %% Kate logs in for first time and ask for inbox
  374:         escalus:send(Kate, Stanza),
  375:         [ResIQ] = escalus:wait_for_stanzas(Kate, 1),
  376:         true = escalus_pred:is_iq_result(ResIQ),
  377:         %% Inbox is empty
  378:         TotalCount = inbox_helper:get_result_el(ResIQ, <<"count">>),
  379:         0 = TotalCount
  380:       end).
  381: 
  382: msg_sent_stored_in_inbox(Config) ->
  383:     test_msg_stored_in_inbox(Config, undefined).
  384: 
  385: msg_sent_stored_in_inbox_iq_id_as_queryid_fallback(Config) ->
  386:     test_msg_stored_in_inbox(Config, iq_id).
  387: 
  388: msg_sent_stored_in_inbox_queryid(Config) ->
  389:     test_msg_stored_in_inbox(Config, queryid).
  390: 
  391: test_msg_stored_in_inbox(Config, QueryId) ->
  392:     escalus:fresh_story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) ->
  393:         #{ Alice := AliceConvs, Bob := BobConvs } = given_conversations_between(Alice, [Bob]),
  394:         %% Both check inbox
  395:         check_inbox(Alice, AliceConvs, inbox_helper:maybe_make_queryid(QueryId)),
  396:         check_inbox(Bob, BobConvs, inbox_helper:maybe_make_queryid(QueryId))
  397:       end).
  398: 
  399: msg_with_no_store_is_not_stored_in_inbox(Config) ->
  400:     escalus:fresh_story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) ->
  401:         %% Alice sends a message to Bob with a no-store hint
  402:         Body = <<"test">>,
  403:         Msg1 = escalus_stanza:chat_to(Bob, Body),
  404:         Msg2 = escalus_stanza:set_id(Msg1, escalus_stanza:id()),
  405:         Msg3 = mam_helper:add_nostore_hint(Msg2),
  406:         escalus:send(Alice, Msg3),
  407:         MsgSent = escalus:wait_for_stanza(Bob),
  408:         escalus:assert(is_chat_message, MsgSent),
  409:         %% Bob has no unread messages in conversation with Alice
  410:         check_inbox(Bob, []),
  411:         %% Alice has no conv in her inbox either
  412:         check_inbox(Alice, [])
  413:       end).
  414: 
  415: msg_with_store_hint_is_always_stored(Config) ->
  416:     escalus:fresh_story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) ->
  417:         %% Alice sends a message to Bob with a store hint, that would otherwise be ignored
  418:         Msg1 = escalus_stanza:to(#xmlel{name = <<"message">>}, Bob),
  419:         Msg2 = escalus_stanza:set_id(Msg1, escalus_stanza:id()),
  420:         Msg3 = mam_helper:add_store_hint(Msg2),
  421:         escalus:send(Alice, Msg3),
  422:         escalus:wait_for_stanza(Bob),
  423:         %% Alice and Bob has a body-less message in their inbox
  424:         check_inbox(Bob, [#conv{unread = 1, from = Alice, to = Bob, content = <<>>}]),
  425:         check_inbox(Alice, [#conv{unread = 0, from = Alice, to = Bob, content = <<>>}])
  426:       end).
  427: 
  428: carbons_are_not_stored(Config) ->
  429:     escalus:fresh_story(Config, [{alice, 2}, {bob, 2}], fun(Alice1, Alice2, Bob1, Bob2) ->
  430:         mongoose_helper:enable_carbons([Alice1, Alice2, Bob1, Bob2]),
  431:         #{ Alice1 := AliceConvs, Bob1 := BobConvs } = given_conversations_between(Alice1, [Bob1]),
  432:         %% Both check inbox and carbons aren't there
  433:         check_inbox(Alice1, AliceConvs),
  434:         check_inbox(Bob1, BobConvs)
  435:       end).
  436: 
  437: user_has_two_conversations(Config) ->
  438:     escalus:fresh_story(Config, [{alice, 1}, {bob, 1}, {kate, 1}], fun(Alice, Bob, Kate) ->
  439:         #{ Alice := AliceConvs, Bob := BobConvs, Kate := KateConvs } =
  440:         given_conversations_between(Alice, [Bob, Kate]),
  441: 
  442:         %% Alice has two conversations in her inbox (no unread messages)
  443:         check_inbox(Alice, AliceConvs),
  444: 
  445:         %% Kate has one conversation with one unread
  446:         check_inbox(Kate, KateConvs),
  447: 
  448:         %% Bob has one conversation with one unread
  449:         check_inbox(Bob, BobConvs)
  450:       end).
  451: 
  452: user_has_only_unread_messages_or_only_read(Config) ->
  453:     escalus:fresh_story(Config, [{alice, 1}, {bob, 1}, {kate, 1}], fun(Alice, Bob, Kate) ->
  454:         given_conversations_between(Alice, [Bob, Kate]),
  455:         % Alice has no unread messages, but requests all conversations
  456:         inbox_helper:get_inbox(Alice, #{ hidden_read => false }, #{count => 2}),
  457: 
  458:         % Requests only conversations with unread messages
  459:         inbox_helper:get_inbox(Alice, #{ hidden_read => true }, #{count => 0}),
  460:         % Bob has only one, unread message
  461:         inbox_helper:get_inbox(Bob, #{ hidden_read => true }, #{count => 1})
  462:       end).
  463: 
  464: msg_sent_to_offline_user(Config) ->
  465:     escalus:fresh_story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) ->
  466:         %% Bob goes offline
  467:         mongoose_helper:logout_user(Config, Bob),
  468: 
  469:         %% Alice sends a message to Bob
  470:         Msg1 = escalus_stanza:chat_to(Bob, <<"test">>),
  471:         escalus:send(Alice, Msg1),
  472: 
  473:         %% Bob goes online again
  474:         {_, BobSpecs} = extract_user_specs(Bob),
  475:         {ok, NewBob} = escalus_client:start(Config, BobSpecs, <<"new-session">>),
  476:         escalus:send(NewBob, escalus_stanza:presence(<<"available">>)),
  477:         Stanzas = escalus:wait_for_stanzas(NewBob, 2),
  478: 
  479:         escalus_new_assert:mix_match
  480:         ([is_presence, fun(Stanza) -> escalus_pred:is_chat_message(<<"test">>, Stanza) end],
  481:           Stanzas),
  482: 
  483:         %% Alice has conversation
  484:         check_inbox(Alice, [#conv{unread = 0, from = Alice, to = Bob, content = <<"test">>}]),
  485: 
  486:         %% Both check inbox and has a conversation
  487:         check_inbox(NewBob, [#conv{unread = 1, from = Alice, to = Bob, content = <<"test">>}])
  488:       end).
  489: 
  490: msg_sent_to_not_existing_user(Config) ->
  491:     escalus:fresh_story(Config, [{alice, 1}], fun(Alice) ->
  492:         %% Alice sends message to user that doesnt exist
  493:         Msg1 = escalus_stanza:chat_to(<<"not_existing_user@localhost">>, <<"test2">>),
  494:         escalus:send(Alice, Msg1),
  495: 
  496:         %% Alice receives error
  497:         ServiceUnavailable = escalus:wait_for_stanza(Alice),
  498:         escalus_pred:is_error(<<"cancel">>,<<"service-unavailable">>, ServiceUnavailable),
  499: 
  500:         %% Alice has this conversation in inbox.
  501:         check_inbox(Alice,[#conv{unread = 0,
  502:                                  from = Alice,
  503:                                  to = <<"not_existing_user@localhost">>,
  504:                                  content = <<"test2">>}])
  505:       end).
  506: 
  507: user_has_two_unread_messages(Config) ->
  508:     escalus:fresh_story(Config, [{kate, 1}, {mike, 1}], fun(Kate, Mike) ->
  509:         inbox_helper:send_msg(Kate, Mike, "Hello"),
  510:         inbox_helper:send_msg(Kate, Mike, "How are you"),
  511:         %% Mike has two unread messages in conversation with Kate
  512:         check_inbox(Mike, [#conv{unread = 2, from = Kate, to = Mike, content = <<"How are you">>}]),
  513:         %% Kate has one conv in her inbox (no unread messages)
  514:         check_inbox(Kate, [#conv{unread = 0, from = Kate, to = Mike, content = <<"How are you">>}])
  515:       end).
  516: 
  517: other_resources_do_not_interfere(Config) ->
  518:     %% regression test
  519:     escalus:fresh_story(Config, [{kate, 2}, {mike, 1}], fun(Kate, Kate2, Mike) ->
  520:         Prio = #xmlel{name = <<"priority">>, children = [#xmlcdata{content = <<"100">>}]},
  521:         escalus_client:send(Kate2, escalus_stanza:presence(<<"available">>, [Prio])),
  522:         escalus_client:wait_for_stanza(Kate),
  523:         inbox_helper:send_msg(Kate, Mike, "Hello"),
  524:         inbox_helper:send_msg(Kate, Mike, "How are you"),
  525:         %% Mike has two unread messages in conversation with Kate
  526:         check_inbox(Mike, [#conv{unread = 2, from = Kate, to = Mike, content = <<"How are you">>}]),
  527:         %% Kate has one conv in her inbox (no unread messages)
  528:         check_inbox(Kate, [#conv{unread = 0, from = Kate, to = Mike, content = <<"How are you">>}])
  529:                                                   end).
  530: 
  531: reset_unread_counter_with_reset_chat_marker(Config) ->
  532:     escalus:fresh_story(Config, [{kate, 1}, {mike, 1}], fun(Kate, Mike) ->
  533:         Msg = inbox_helper:send_msg(Kate, Mike, <<"Hi mike">>),
  534:         MsgId = exml_query:attr(Msg, <<"id">>),
  535: 
  536:         %% Mike has one unread message
  537:         check_inbox(Mike, [#conv{unread = 1, from = Kate, to = Mike, content = <<"Hi mike">>}]),
  538:         ChatMarker = escalus_stanza:chat_marker(Kate, <<"displayed">>, MsgId),
  539:         %% Mike sends "displayed" chat marker to Kate
  540:         escalus:send(Mike, ChatMarker),
  541:         %% Now Mike asks for inbox second time. He has 0 unread messages now
  542:         check_inbox(Mike, [#conv{unread = 0, from = Kate, to = Mike, content = <<"Hi mike">>}])
  543:       end).
  544: 
  545: reset_unread_counter_with_reset_stanza(Config) ->
  546:     escalus:fresh_story(Config, [{kate, 1}, {mike, 1}], fun(Kate, Mike) ->
  547:         _Msg = inbox_helper:send_msg(Kate, Mike, <<"Hi mike">>),
  548: 
  549:         %% Mike has one unread message
  550:         check_inbox(Mike, [#conv{unread = 1, from = Kate, to = Mike, content = <<"Hi mike">>}]),
  551:         %% Mike sends "reset" stanza
  552:         inbox_helper:reset_inbox(Mike, Kate),
  553:         %% Now Mike asks for inbox second time. He has 0 unread messages now
  554:         check_inbox(Mike, [#conv{unread = 0, from = Kate, to = Mike, content = <<"Hi mike">>}]),
  555:         %% Kate should not be receiving this stanza
  556:         inbox_helper:assert_has_no_stanzas(Kate)
  557:       end).
  558: 
  559: reset_unread_counter_and_show_only_unread(Config) ->
  560:     escalus:fresh_story(Config, [{kate, 1}, {mike, 1}, {alice, 1}], fun(Kate, Mike, Alice) ->
  561:         Msg = inbox_helper:send_msg(Kate, Mike),
  562:         MsgId = exml_query:attr(Msg, <<"id">>),
  563:         inbox_helper:get_inbox(Mike, #{ hidden_read => true }, #{count => 1}),
  564: 
  565:         ChatMarker = escalus_stanza:chat_marker(Kate, <<"displayed">>, MsgId),
  566:         escalus:send(Mike, ChatMarker),
  567: 
  568:         inbox_helper:get_inbox(Mike, #{ hidden_read => true }, #{count => 0}),
  569: 
  570:         inbox_helper:send_msg(Alice, Mike),
  571:         % Mike has two conversations, one with unread messages
  572:         inbox_helper:get_inbox(Mike, #{ hidden_read => true }, #{count => 1}),
  573:         inbox_helper:get_inbox(Mike, #{ hidden_read => false }, #{count => 2})
  574:       end).
  575: 
  576: check_total_unread_count_and_active_conv_count(Config) ->
  577:     escalus:fresh_story(Config, [{kate, 1}, {mike, 1}, {alice, 1}], fun(Kate, Mike, Alice) ->
  578:         inbox_helper:send_and_mark_msg(Kate, Mike),
  579:         inbox_helper:send_msg(Alice, Mike),
  580:         inbox_helper:send_msg(Alice, Mike),
  581:         inbox_helper:send_msg(Kate, Mike),
  582:         inbox_helper:get_inbox(Mike, #{count => 2, unread_messages => 3, active_conversations => 2})
  583:       end).
  584: 
  585: check_total_unread_count_when_there_are_no_active_conversations(Config) ->
  586:     escalus:fresh_story(Config, [{kate, 1}, {mike, 1}], fun(Kate, Mike) ->
  587:         inbox_helper:send_and_mark_msg(Kate, Mike),
  588:         inbox_helper:get_inbox(Mike, #{count => 1, unread_messages => 0, active_conversations => 0})
  589:     end).
  590: 
  591: total_unread_count_and_active_convs_are_zero_at_no_activity(Config) ->
  592:     escalus:fresh_story(Config, [{kate, 1}], fun(Kate) ->
  593:         inbox_helper:get_inbox(Kate, #{count => 0, unread_messages => 0, active_conversations => 0})
  594:     end).
  595: 
  596: inbox_result_cannot_exceed_configured_limit(Config) ->
  597:     escalus:fresh_story(Config, [{alice, 1}, {bob, 1}, {kate, 1}, {mike, 1}], fun(Alice, Bob, Kate, Mike) ->
  598:         inbox_helper:send_msg(Alice, Bob),
  599:         inbox_helper:send_msg(Kate, Bob),
  600:         inbox_helper:send_msg(Mike, Bob),
  601:         inbox_helper:get_inbox(Bob, #{count => 2})
  602:       end).
  603: 
  604: should_apply_lower_limit_when_both_provided(Config) ->
  605:     escalus:fresh_story(Config, [{alice, 1}, {bob, 1}, {kate, 1}, {mike, 1}], fun(Alice, Bob, Kate, Mike) ->
  606:         inbox_helper:send_msg(Alice, Bob),
  607:         inbox_helper:send_msg(Kate, Bob),
  608:         inbox_helper:send_msg(Mike, Bob),
  609:         inbox_helper:get_inbox(Bob, #{limit => 1}, #{count => 1})
  610:       end).
  611: 
  612: try_to_reset_unread_counter_with_bad_marker(Config) ->
  613:     escalus:fresh_story(Config, [{kate, 1}, {mike, 1}], fun(Kate, Mike) ->
  614:         Msg1 = escalus_stanza:set_id(escalus_stanza:chat_to(Mike, <<"okey dockey">>), <<"111">>),
  615:         %% Kate sends message to Mike
  616:         escalus:send(Kate, Msg1),
  617:         M1 = escalus:wait_for_stanza(Mike),
  618:         escalus:assert(is_chat_message, M1),
  619:         %% Mike has one unread message
  620:         check_inbox(Mike, [#conv{unread = 1, from = Kate, to = Mike, content = <<"okey dockey">>}]),
  621:         MsgId = <<"badId">>,
  622:         ChatMarker = escalus_stanza:chat_marker(Kate, <<"displayed">>, MsgId),
  623:         %% Mike sends "displayed" chat marker but 'id' field is wrong
  624:         escalus:send(Mike, ChatMarker),
  625:         %% Now Mike asks for inbox second time. Unread count should be still the same
  626:         check_inbox(Mike, [#conv{unread = 1, from = Kate, to = Mike, content = <<"okey dockey">>}])
  627:       end).
  628: 
  629: non_reset_marker_should_not_affect_inbox(Config) ->
  630:     escalus:fresh_story(Config, [{kate, 1}, {mike, 1}], fun(Kate, Mike) ->
  631:         MsgId = <<"kate_to_mike">>,
  632:         MsgBody = <<"okey dockey">>,
  633:         Msg = escalus_stanza:set_id(escalus_stanza:chat_to(Mike, MsgBody), MsgId),
  634:         %% Kate sends message to Mike
  635:         escalus:send(Kate, Msg),
  636:         M1 = escalus:wait_for_stanza(Mike),
  637:         escalus:assert(is_chat_message, M1),
  638:         %% Mike has one unread message
  639:         check_inbox(Mike, [#conv{unread = 1, from = Kate, to = Mike, content = MsgBody}]),
  640:         ChatMarker = escalus_stanza:chat_marker(Kate, <<"received">>, MsgId),
  641:         %% Mike sends "received" chat marker, which is not a reset_marker
  642:         escalus:send(Mike, ChatMarker),
  643:         CM = escalus:wait_for_stanza(Kate),
  644:         escalus:assert(is_message, CM),
  645:         %% Now Mike asks for inbox second time. Unread count should be still the same
  646:         check_inbox(Mike, [#conv{unread = 1, from = Kate, to = Mike, content = MsgBody}]),
  647:         check_inbox(Kate, [], #{hidden_read => true}, #{})
  648:       end).
  649: 
  650: %%--------------------------------------------------------------------
  651: %% Inbox tests muclight
  652: %%--------------------------------------------------------------------
  653: 
  654: simple_groupchat_stored_in_all_inbox(Config) ->
  655:     escalus:fresh_story(Config, [{alice, 1}, {bob, 1}, {kate, 1}], fun(Alice, Bob, Kate) ->
  656:         Msg = <<"Hi Room!">>,
  657:         Id = <<"MyID">>,
  658:         Users = [Alice, Bob, Kate],
  659:         Room = inbox_helper:create_room(Alice, [Bob, Kate]),
  660:         AliceJid = inbox_helper:to_bare_lower(Alice),
  661:         KateJid = inbox_helper:to_bare_lower(Kate),
  662:         BobJid = inbox_helper:to_bare_lower(Bob),
  663:         RoomJid = room_bin_jid(Room),
  664:         AliceRoomJid = <<RoomJid/binary, "/", AliceJid/binary>>,
  665:         Stanza = escalus_stanza:set_id(
  666:                    escalus_stanza:groupchat_to(RoomJid, Msg), Id),
  667:         %% Alice sends message to a room
  668:         escalus:send(Alice, Stanza),
  669:         inbox_helper:wait_for_groupchat_msg(Users),
  670:         %% Alice has 0 unread messages
  671:         check_inbox(Alice, [#conv{unread = 0, from = AliceRoomJid, to = AliceJid, content = Msg}]),
  672:         %% Bob and Kate have one conv with 1 unread message
  673:         check_inbox(Bob, [#conv{unread = 1, from = AliceRoomJid, to = BobJid, content = Msg}]),
  674:         check_inbox(Kate, [#conv{unread = 1, from = AliceRoomJid, to = KateJid, content = Msg}])
  675:       end).
  676: 
  677: advanced_groupchat_stored_in_all_inbox(Config) ->
  678:     escalus:fresh_story(Config, [{alice, 1}, {bob, 1}, {kate, 1}], fun(Alice, Bob, Kate) ->
  679:         Msg1 = <<"Hi Room!">>,
  680:         Msg2 = <<"How are you?">>,
  681:         Id1 = <<"id-1">>,
  682:         Id2 = <<"id-2">>,
  683:         Users = [Alice, Bob, Kate],
  684:         Room = pubsub_tools:pubsub_node_name(),
  685:         inbox_helper:create_room_and_check_inbox(Alice, [Bob, Kate], Room),
  686:         BobJid = inbox_helper:to_bare_lower(Bob),
  687:         AliceJid = inbox_helper:to_bare_lower(Alice),
  688:         KateJid = inbox_helper:to_bare_lower(Kate),
  689:         RoomJid = room_bin_jid(Room),
  690:         BobRoomJid = <<RoomJid/binary, "/", BobJid/binary>>,
  691:         Stanza1 = escalus_stanza:set_id(
  692:                     escalus_stanza:groupchat_to(RoomJid, Msg1), Id1),
  693:         %% Alice sends msg to room
  694:         escalus:send(Alice, Stanza1),
  695:         inbox_helper:wait_for_groupchat_msg(Users),
  696:         %% Bob sends second message
  697:         Stanza2 = escalus_stanza:set_id(
  698:                     escalus_stanza:groupchat_to(RoomJid, Msg2), Id2),
  699:         escalus:send(Bob, Stanza2),
  700:         inbox_helper:wait_for_groupchat_msg(Users),
  701:         %% Alice have one unread message (from Bob)
  702:         check_inbox(Alice, [#conv{unread = 1, from = BobRoomJid, to = AliceJid, content = Msg2}]),
  703:         %% Bob has 0 unread messages because he sent the last message
  704:         check_inbox(Bob, [#conv{unread = 0, from = BobRoomJid, to = BobJid, content = Msg2}]),
  705:         %% Kate has 2 unread messages (from Alice and Bob)
  706:         check_inbox(Kate, [#conv{unread = 2, from = BobRoomJid, to = KateJid, content = Msg2}])
  707:       end).
  708: 
  709: groupchat_markers_one_reset(Config) ->
  710:     escalus:fresh_story(Config, [{alice, 1}, {bob, 1}, {kate, 1}], fun(Alice, Bob, Kate) ->
  711:         AliceJid = inbox_helper:to_bare_lower(Alice),
  712:         BobJid = inbox_helper:to_bare_lower(Bob),
  713:         KateJid = inbox_helper:to_bare_lower(Kate),
  714:         Room = inbox_helper:create_room(Alice, [Bob, Kate]),
  715:         RoomJid = room_bin_jid(Room),
  716:         AliceRoomJid = <<RoomJid/binary, "/", AliceJid/binary>>,
  717:         Id = <<"markerId">>,
  718:         Users = [Alice, Bob, Kate],
  719:         Stanza1 = escalus_stanza:set_id(
  720:                     escalus_stanza:groupchat_to(RoomJid, <<"marker time!">>), Id),
  721:         escalus:send(Alice, Stanza1),
  722:         inbox_helper:wait_for_groupchat_msg(Users),
  723:         ChatMarkerWOType = escalus_stanza:chat_marker(RoomJid,<<"displayed">>, Id),
  724:         ChatMarker = escalus_stanza:setattr(ChatMarkerWOType, <<"type">>, <<"groupchat">>),
  725:         %% User marks last message
  726:         escalus:send(Bob, ChatMarker),
  727:         %% participants receive marker
  728:         muc_helper:foreach_recipient([Alice, Bob, Kate], fun(Marker) ->
  729:           true = escalus_pred:is_chat_marker(<<"displayed">>, Id, Marker) end),
  730:         %% Bob has 0 unread messages because he reset his counter
  731:         check_inbox(Bob, [#conv{unread = 0, from = AliceRoomJid,
  732:                                 to = BobJid, content = <<"marker time!">>}]),
  733:         %% Alice has 0 unread messages because she was the sender
  734:         check_inbox(Alice, [#conv{unread = 0, from = AliceRoomJid,
  735:                                   to = AliceJid, content = <<"marker time!">>}]),
  736:         %% Kate still has unread message
  737:         check_inbox(Kate, [#conv{unread = 1, from = AliceRoomJid,
  738:                                  to = KateJid, content = <<"marker time!">>}])
  739:       end).
  740: 
  741: non_reset_marker_should_not_affect_muclight_inbox(Config) ->
  742:     escalus:fresh_story(Config, [{alice, 1}, {bob, 1}, {kate, 1}], fun(Alice, Bob, Kate) ->
  743:         % %% GIVEN
  744:         AliceJid = inbox_helper:to_bare_lower(Alice),
  745:         BobJid = inbox_helper:to_bare_lower(Bob),
  746:         KateJid = inbox_helper:to_bare_lower(Kate),
  747:         % %% WHEN DONE
  748:         Id = <<"some_ID">>,
  749:         Msg = <<"marker time!">>,
  750:         RoomName = pubsub_tools:pubsub_node_name(),
  751:         RoomJid = inbox_helper:create_room_send_msg_check_inbox(Alice, [Bob, Kate], RoomName, Msg, Id),
  752:         AliceRoomJid = <<RoomJid/binary, "/", AliceJid/binary>>,
  753:         % %% AND MARKED WRONG
  754:         inbox_helper:mark_last_muclight_message(Bob, [Alice, Bob, Kate], <<"received">>),
  755:         inbox_helper:mark_last_muclight_message(Kate, [Alice, Bob, Kate], <<"acknowledged">>),
  756:         % %% THEN
  757:         %% Alice has 0 unread messages because she was the sender
  758:         check_inbox(Alice, [#conv{unread = 0, from = AliceRoomJid,
  759:                                   to = AliceJid, content = Msg}]),
  760:         %% Bob still has unread message
  761:         check_inbox(Bob, [#conv{unread = 1, from = AliceRoomJid,
  762:                                 to = BobJid, content = Msg}]),
  763:         %% Kate still has unread message
  764:         check_inbox(Kate, [#conv{unread = 1, from = AliceRoomJid,
  765:                                  to = KateJid, content = Msg}])
  766:       end).
  767: 
  768: groupchat_reset_stanza_resets_inbox(Config) ->
  769:     escalus:fresh_story(Config, [{alice, 1}, {bob, 1}, {kate, 1}], fun(Alice, Bob, Kate) ->
  770:         % %% WITH
  771:         Id = <<"some_ID">>,
  772:         Msg = <<"marker time!">>,
  773:         RoomName = pubsub_tools:pubsub_node_name(),
  774:         AliceJid = inbox_helper:to_bare_lower(Alice),
  775:         BobJid = inbox_helper:to_bare_lower(Bob),
  776:         KateJid = inbox_helper:to_bare_lower(Kate),
  777:         % %% WHEN A MESSAGE IS SENT
  778:         RoomJid = inbox_helper:create_room_send_msg_check_inbox(Alice, [Bob, Kate], RoomName, Msg, Id),
  779:         AliceRoomJid = <<RoomJid/binary, "/", AliceJid/binary>>,
  780:         % %% AND WHEN SEND RESET FOR ROOM
  781:         inbox_helper:reset_inbox(Bob, RoomJid),
  782:         % %% THEN INBOX IS RESET FOR BOB, WITHOUT FORWARDING
  783:         %% Bob has 0 unread messages because he reset his counter
  784:         check_inbox(Bob, [#conv{unread = 0, from = AliceRoomJid,
  785:                                 to = BobJid, content = <<"marker time!">>}]),
  786:         %% Alice has 0 unread messages because she was the sender
  787:         check_inbox(Alice, [#conv{unread = 0, from = AliceRoomJid,
  788:                                   to = AliceJid, content = <<"marker time!">>}]),
  789:         %% Kate still has unread message
  790:         check_inbox(Kate, [#conv{unread = 1, from = AliceRoomJid,
  791:                                  to = KateJid, content = <<"marker time!">>}]),
  792:         %% And nobody received any other stanza
  793:         inbox_helper:assert_has_no_stanzas([Alice, Bob, Kate])
  794:       end).
  795: 
  796: %% this test combines options:
  797: %% ...
  798: %%{aff_changes, true},
  799: %%{remove_on_kicked, true},
  800: create_groupchat(Config) ->
  801:     escalus:fresh_story(Config, [{alice, 1}, {bob, 1}, {kate, 1}], fun(Alice, Bob, Kate) ->
  802:         RoomName = pubsub_tools:pubsub_node_name(),
  803:         inbox_helper:create_room_and_check_inbox(Bob, [Alice, Kate], RoomName)
  804:       end).
  805: 
  806: %% this test combines options:
  807: %% ...
  808: %%{aff_changes, false},
  809: %%{remove_on_kicked, true},
  810: create_groupchat_no_affiliation_stored(Config) ->
  811:     escalus:story(Config, [{alice, 1}, {bob, 1}, {kate, 1}], fun(Alice, Bob, Kate) ->
  812:         InitOccupants = [{Alice, member}, {Kate, member}],
  813:         FinalOccupants = [{Bob, owner} | InitOccupants],
  814:         Msg = <<"Hi all!">>,
  815:         Users = [Alice, Bob, Kate],
  816:         InitConfig = [{<<"roomname">>, <<"Bob's room2">>}],
  817:         RoomNode = <<"bobroom2">>,
  818:         RoomJid = room_bin_jid(RoomNode),
  819:         AliceJid = inbox_helper:to_bare_lower(Alice),
  820:         AliceRoomJid = <<RoomJid/binary, "/", AliceJid/binary>>,
  821:         BobJid = inbox_helper:to_bare_lower(Bob),
  822:         KateJid = inbox_helper:to_bare_lower(Kate),
  823:         %% Bob creates room
  824:         escalus:send(Bob, muc_light_helper:stanza_create_room(RoomNode, InitConfig, InitOccupants)),
  825:         muc_light_helper:verify_aff_bcast(FinalOccupants, FinalOccupants),
  826:         escalus:assert(is_iq_result, escalus:wait_for_stanza(Bob)),
  827:         %% affiliation change messages are not stored in inbox
  828:         [ check_inbox(User, []) || User <- Users ],
  829:         Stanza = escalus_stanza:set_id(
  830:                    escalus_stanza:groupchat_to(RoomJid, Msg), <<"123">>),
  831:         %% Alice sends a message
  832:         escalus:send(Alice, Stanza),
  833:         inbox_helper:wait_for_groupchat_msg(Users),
  834:         %% Alice has 0 unread because she sent the last message
  835:         check_inbox(Alice, [#conv{unread = 0, from = AliceRoomJid, to = AliceJid, content = Msg}]),
  836:         %% Bob and Kate have one unread message
  837:         check_inbox(Bob, [#conv{unread = 1, from = AliceRoomJid, to = BobJid, content = Msg}]),
  838:         check_inbox(Kate, [#conv{unread = 1, from = AliceRoomJid, to = KateJid, content = Msg}])
  839:     end).
  840: 
  841: %% this test combines options:
  842: %% ...
  843: %%{aff_changes, true},
  844: %%{remove_on_kicked, true},
  845: leave_and_remove_conversation(Config) ->
  846:     escalus:fresh_story(Config, [{alice, 1}, {bob, 1}, {kate, 1}], fun(Alice, Bob, Kate) ->
  847:         RoomName = pubsub_tools:pubsub_node_name(),
  848:         AliceJid = inbox_helper:to_bare_lower(Alice),
  849:         BobJid = inbox_helper:to_bare_lower(Bob),
  850:         KateJid = inbox_helper:to_bare_lower(Kate),
  851:         %% Alice creates a room and send msg
  852:         Msg = <<"Hi all">>,
  853:         Id = <<"leave-id">>,
  854:         RoomJid = inbox_helper:create_room_send_msg_check_inbox(Alice, [Bob, Kate], RoomName, Msg, Id),
  855:         AliceRoomJid = <<RoomJid/binary, "/", AliceJid/binary>>,
  856:         %% Bob leaves the room
  857:         muc_light_helper:user_leave(RoomName, Bob, [{Alice, owner}, {Kate, member}]),
  858:         %% Alice and Kate have one message
  859:         check_inbox(Alice, [#conv{unread = 0, from = AliceRoomJid, to = AliceJid, content = Msg}]),
  860:         check_inbox(Kate, [#conv{unread = 1, from = AliceRoomJid, to = KateJid, content = Msg}]),
  861:         %% Bob doesn't have conversation in his inbox
  862:         check_inbox(Bob, [], #{box => inbox}),
  863:         if_async_check_bin(Config, Bob, [#conv{unread = 2, from = RoomJid, to = BobJid,
  864:                                                verify = fun inbox_helper:verify_is_none_aff_change/2}])
  865:     end).
  866: 
  867: %% this test combines options:
  868: %% ...
  869: %%{aff_changes, true},
  870: %%{remove_on_kicked, false},
  871: leave_and_store_conversation(Config) ->
  872:     escalus:story(Config, [{alice, 1}, {bob, 1}, {kate, 1}], fun(Alice, Bob, Kate) ->
  873:         RoomName = pubsub_tools:pubsub_node_name(),
  874:         AliceJid = inbox_helper:to_bare_lower(Alice),
  875:         BobJid = inbox_helper:to_bare_lower(Bob),
  876:         KateJid = inbox_helper:to_bare_lower(Kate),
  877:         %% Alice creates a room and send msg
  878:         Msg = <<"Hi all">>,
  879:         Id = <<"leave-id">>,
  880:         RoomJid = inbox_helper:create_room_send_msg_check_inbox(Alice, [Bob, Kate], RoomName, Msg, Id),
  881:         AliceRoomJid = <<RoomJid/binary, "/", AliceJid/binary>>,
  882:         %% Bob leaves room
  883:         muc_light_helper:user_leave(RoomName, Bob, [{Alice, owner}, {Kate, member}]),
  884:         %% Alice and Kate have conversation
  885:         check_inbox(Alice, [#conv{unread = 0, from = AliceRoomJid, to = AliceJid, content = Msg}]),
  886:         check_inbox(Kate, [#conv{unread = 1, from = AliceRoomJid, to = KateJid, content = Msg}]),
  887:         %% Bob still has a conversation in inbox. Two unread - first is invitation,
  888:         %% the second is the leaving affiliation
  889:         check_inbox(Bob, [#conv{unread = 2, from = RoomJid, to = BobJid,
  890:                                 verify = fun inbox_helper:verify_is_none_aff_change/2}])
  891:       end).
  892: 
  893: %% this test combines options:
  894: %% ...
  895: %%{aff_changes, false},
  896: %%{remove_on_kicked, true},
  897: no_aff_stored_and_remove_on_kicked(Config) ->
  898:     escalus:story(Config, [{alice, 1}, {bob, 1}, {kate, 1}], fun(Alice, Bob, Kate) ->
  899:         AliceJid = inbox_helper:to_bare_lower(Alice),
  900:         BobJid = inbox_helper:to_bare_lower(Bob),
  901:         KateJid = inbox_helper:to_bare_lower(Kate),
  902:         RoomJid = room_bin_jid(?ROOM3),
  903:         AliceRoomJid = <<RoomJid/binary, "/", AliceJid/binary>>,
  904:         Msg = <<"Hi all">>,
  905:         Users = [Alice, Bob, Kate],
  906:         Stanza = escalus_stanza:set_id(
  907:           escalus_stanza:groupchat_to(RoomJid, Msg), <<"33">>),
  908:         %% Alice sends a message
  909:         escalus:send(Alice, Stanza),
  910:         inbox_helper:wait_for_groupchat_msg(Users),
  911:         %% Bob leaves the room
  912:         muc_light_helper:user_leave(?ROOM3, Bob, [{Alice, owner}, {Kate, member}]),
  913:         %% Alice and Kate have message in groupchats
  914:         check_inbox(Alice, [#conv{unread = 0, from = AliceRoomJid, to = AliceJid, content = Msg}]),
  915:         check_inbox(Kate, [#conv{unread = 1, from = AliceRoomJid, to = KateJid, content = Msg}]),
  916:         %% Bob doesnt have a conversation in inbox
  917:         check_inbox(Bob, [], #{box => inbox}),
  918:         if_async_check_bin(Config, Bob, [#conv{unread = 1, from = AliceRoomJid, to = BobJid, content = Msg}])
  919:       end).
  920: 
  921: %% this test combines options:
  922: %% ...
  923: %%{aff_changes, true},
  924: %%{remove_on_kicked, false},
  925: no_stored_and_remain_after_kicked(Config) ->
  926:     escalus:story(Config, [{alice, 1}, {bob, 1}, {kate, 1}], fun(Alice, Bob, Kate) ->
  927:         AliceJid = inbox_helper:to_bare_lower(Alice),
  928:         KateJid = inbox_helper:to_bare_lower(Kate),
  929:         BobJid = inbox_helper:to_bare_lower(Bob),
  930:         RoomJid = room_bin_jid(?ROOM4),
  931:         AliceRoomJid = <<RoomJid/binary, "/", AliceJid/binary>>,
  932:         Msg = <<"Hi all">>,
  933:         Users = [Alice, Bob, Kate],
  934:         Stanza = escalus_stanza:set_id(
  935:           escalus_stanza:groupchat_to(RoomJid, Msg), <<"33">>),
  936:         %% Alice sends a message
  937:         escalus:send(Alice, Stanza),
  938:         inbox_helper:wait_for_groupchat_msg(Users),
  939:         %% Bob leaves the room
  940:         muc_light_helper:user_leave(?ROOM4, Bob, [{Alice, owner}, {Kate, member}]),
  941:         %% Alice and Kate have message in groupchats
  942:         check_inbox(Alice, [#conv{unread = 0, from = AliceRoomJid, to = AliceJid, content = Msg}]),
  943:         check_inbox(Kate, [#conv{unread = 1, from = AliceRoomJid, to = KateJid, content = Msg}]),
  944:         %% Bob have a conversation in inbox. First unread is message from Alice, the second the affiliation change
  945:         check_inbox(Bob, [#conv{unread = 2, from = RoomJid, to = BobJid,
  946:                                 verify = fun inbox_helper:verify_is_none_aff_change/2}])
  947:       end).
  948: 
  949: groupchat_markers_one_reset_room_created(Config) ->
  950:     escalus:fresh_story(Config, [{alice, 1}, {bob, 1}, {kate, 1}], fun(Alice, Bob, Kate) ->
  951:         Id = <<"random-id">>,
  952:         Msg = <<"Welcome guys">>,
  953:         RoomName = pubsub_tools:pubsub_node_name(),
  954:         AliceJid = inbox_helper:to_bare_lower(Alice),
  955:         BobJid = inbox_helper:to_bare_lower(Bob),
  956:         KateJid = inbox_helper:to_bare_lower(Kate),
  957:         RoomJid = inbox_helper:create_room_send_msg_check_inbox(Alice, [Bob, Kate], RoomName, Msg, Id),
  958:         AliceRoomJid = <<RoomJid/binary, "/", AliceJid/binary>>,
  959:         %% Now Bob sends marker
  960:         inbox_helper:mark_last_muclight_message(Bob, [Alice, Bob, Kate]),
  961:         %% The crew ask for inbox second time. Only Kate has unread messages
  962:         check_inbox(Alice,[#conv{unread = 0, from = AliceRoomJid, to = AliceJid, content = Msg}]),
  963:         check_inbox(Kate, [#conv{unread = 1, from = AliceRoomJid, to = KateJid, content = Msg}]),
  964:         check_inbox(Bob, [#conv{unread = 0, from = AliceRoomJid, to = BobJid, content = Msg}])
  965:       end).
  966: 
  967: groupchat_markers_all_reset_room_created(Config) ->
  968:     escalus:fresh_story(Config, [{alice, 1}, {bob, 1}, {kate, 1}], fun(Alice, Bob, Kate) ->
  969:         Id = <<"random-id">>,
  970:         Msg = <<"Mark me!">>,
  971:         RoomName = pubsub_tools:pubsub_node_name(),
  972:         AliceJid = inbox_helper:to_bare_lower(Alice),
  973:         RoomJid = inbox_helper:create_room_send_msg_check_inbox(Alice, [Bob, Kate], RoomName, Msg, Id),
  974:         AliceRoomJid = <<RoomJid/binary, "/", AliceJid/binary>>,
  975:         [inbox_helper:mark_last_muclight_message(U, [Alice, Bob, Kate]) || U <- [Bob, Kate]],
  976:         inbox_helper:foreach_check_inbox([Bob, Kate, Alice], 0, AliceRoomJid, Msg)
  977:       end).
  978: 
  979: inbox_does_not_trigger_does_user_exist(Config) ->
  980:     escalus:fresh_story(Config, [{alice, 1}, {bob, 1}, {kate, 1}], fun(Alice, Bob, Kate) ->
  981:         Msg = <<"Mark me!">>,
  982:         RoomName = inbox_helper:create_room(Alice, [Bob, Kate]),
  983:         RoomJid = room_bin_jid(RoomName),
  984:         HookHandlerExtra = start_hook_listener(),
  985:         Stanza = escalus_stanza:groupchat_to(RoomJid, Msg),
  986:         %% Alice sends message to a room
  987:         escalus:send(Alice, Stanza),
  988:         [escalus:wait_for_stanza(User) || User <- [Alice, Bob, Kate]],
  989:         stop_hook_listener(HookHandlerExtra),
  990:         verify_hook_listener(RoomName)
  991:       end).
  992: 
  993: system_message_is_correctly_avoided(Config) ->
  994:     escalus:story(Config, [{alice, 1}, {alice_bis, 1}, {bob, 1}], fun(Alice, AliceBis, Bob) ->
  995:         %% Variables
  996:         Id1 = <<"id-1">>,
  997:         Id2 = <<"id-2">>,
  998:         Msg1 = <<"Hi Room!">>,
  999:         Msg2 = <<"How are you?">>,
 1000:         Users = [Alice, AliceBis, Bob],
 1001:         InitOccupants = [{M, member} || M <- [AliceBis, Bob]],
 1002:         Room = atom_to_binary(?FUNCTION_NAME, utf8),
 1003:         BobJid = inbox_helper:to_bare_lower(Bob),
 1004:         AliceJid = inbox_helper:to_bare_lower(Alice),
 1005:         AliceBisJid = inbox_helper:to_bare_lower(AliceBis),
 1006:         RoomJid = room_bin_jid(Room),
 1007:         %% Given a room
 1008:         muc_light_helper:given_muc_light_room(Room, Alice, InitOccupants),
 1009:         BobRoomJid = <<RoomJid/binary, "/", BobJid/binary>>,
 1010:         Stanza1 = escalus_stanza:set_id(escalus_stanza:groupchat_to(RoomJid, Msg1), Id1),
 1011:         %% Alice sends msg to room
 1012:         escalus:send(Alice, Stanza1),
 1013:         inbox_helper:wait_for_groupchat_msg(Users),
 1014:         %% Bob sends second message
 1015:         Stanza2 = escalus_stanza:set_id(escalus_stanza:groupchat_to(RoomJid, Msg2), Id2),
 1016:         escalus:send(Bob, Stanza2),
 1017:         inbox_helper:wait_for_groupchat_msg(Users),
 1018:         %% Alice has one unread message (from Bob)
 1019:         check_inbox(Alice, [#conv{unread = 1, from = BobRoomJid, to = AliceJid, content = Msg2}]),
 1020:         %% Bob has 0 unread messages because he sent the last message
 1021:         check_inbox(Bob, [#conv{unread = 0, from = BobRoomJid, to = BobJid, content = Msg2}]),
 1022:         %% AliceBis has 2 unread messages (from Alice and Bob) and does not have the affiliation
 1023:         check_inbox(AliceBis, [#conv{unread = 2, from = BobRoomJid, to = AliceBisJid, content = Msg2}])
 1024:       end).
 1025: 
 1026: %%--------------------------------------------------------------------
 1027: %% Classic MUC tests
 1028: %%--------------------------------------------------------------------
 1029: 
 1030: simple_groupchat_stored_in_all_inbox_muc(Config) ->
 1031:     escalus:fresh_story(Config, [{alice, 1}, {bob, 1}, {kate, 1}], fun(Alice, Bob, Kate) ->
 1032:         RoomConfig = muc_helper:start_room(Config, extract_user_specs(Alice), muc_helper:fresh_room_name(), <<"some_friendly_name">>, default),
 1033:         Users = [Alice, Bob, Kate],
 1034:         Msg = <<"Hi Room!">>,
 1035:         Id = <<"MyID">>,
 1036:         Room = ?config(room, RoomConfig),
 1037:         RoomAddr = muc_helper:room_address(Room),
 1038: 
 1039:         inbox_helper:enter_room(Room, Users),
 1040:         inbox_helper:make_members(Room, Alice, Users -- [Alice]),
 1041:         Stanza = escalus_stanza:set_id(
 1042:           escalus_stanza:groupchat_to(RoomAddr, Msg), Id),
 1043:         escalus:send(Bob, Stanza),
 1044:         inbox_helper:wait_for_groupchat_msg(Users),
 1045:         [AliceJid, BobJid, KateJid] = lists:map(fun inbox_helper:to_bare_lower/1, Users),
 1046:         BobRoomJid = muc_helper:room_address(Room, inbox_helper:nick(Bob)),
 1047:         %% Bob has 0 unread messages
 1048:         check_inbox(Bob, [#conv{unread = 0, from = BobRoomJid, to = BobJid, content = Msg}],
 1049:                     #{}, #{case_sensitive => true}),
 1050:         %% Alice and Kate have one conv with 1 unread message
 1051:         check_inbox(Alice,[#conv{unread = 1, from = BobRoomJid, to = AliceJid, content = Msg}],
 1052:                     #{}, #{case_sensitive => true}),
 1053:         check_inbox(Kate, [#conv{unread = 1, from = BobRoomJid, to = KateJid, content = Msg}],
 1054:                     #{}, #{case_sensitive => true})
 1055:       end).
 1056: 
 1057: simple_groupchat_stored_in_offline_users_inbox_muc(Config) ->
 1058:     escalus:fresh_story(Config, [{alice, 1}, {bob, 1}, {kate, 1}], fun(Alice, Bob, Kate) ->
 1059:         RoomConfig = muc_helper:start_room(Config, extract_user_specs(Alice), muc_helper:fresh_room_name(), <<"some_friendly_name">>, default),
 1060:         Users = [Alice, Bob, Kate],
 1061:         Msg = <<"Hi Room!">>,
 1062:         Id = <<"MyID">>,
 1063:         Room = ?config(room, RoomConfig),
 1064:         RoomAddr = muc_helper:room_address(Room),
 1065: 
 1066:         inbox_helper:enter_room(Room, Users),
 1067:         inbox_helper:make_members(Room, Alice, Users -- [Alice]),
 1068:         inbox_helper:leave_room(Kate, Room, Users),
 1069:         Stanza = escalus_stanza:set_id(
 1070:           escalus_stanza:groupchat_to(RoomAddr, Msg), Id),
 1071:         escalus:send(Bob, Stanza),
 1072:         inbox_helper:wait_for_groupchat_msg(Users -- [Kate]),
 1073: 
 1074:         [AliceJid, BobJid, KateJid] = lists:map(fun inbox_helper:to_bare_lower/1, Users),
 1075:         BobRoomJid = muc_helper:room_address(Room, inbox_helper:nick(Bob)),
 1076:         %% Bob has 0 unread messages
 1077:         check_inbox(Bob, [#conv{unread = 0, from = BobRoomJid, to = BobJid, content = Msg}],
 1078:                     #{}, #{case_sensitive => true}),
 1079:         %% Alice and Kate have one conv with 1 unread message
 1080:         check_inbox(Alice, [#conv{unread = 1, from = BobRoomJid, to = AliceJid, content = Msg}],
 1081:                     #{}, #{case_sensitive => true}),
 1082:         check_inbox(Kate, [#conv{unread = 1, from = BobRoomJid, to = KateJid, content = Msg}],
 1083:                     #{}, #{case_sensitive => true})
 1084:       end).
 1085: 
 1086: 
 1087: unread_count_is_the_same_after_going_online_again(Config) ->
 1088:     escalus:fresh_story(Config, [{alice, 1}, {bob, 1}, {kate, 1}], fun(Alice, Bob, Kate) ->
 1089:         RoomConfig = muc_helper:start_room(Config, extract_user_specs(Alice), muc_helper:fresh_room_name(), <<"some_friendly_name">>, default),
 1090:         Users = [Alice, Bob, Kate],
 1091:         Msg = <<"Hi Room!">>,
 1092:         Id = <<"MyID">>,
 1093:         Room = ?config(room, RoomConfig),
 1094:         RoomAddr = muc_helper:room_address(Room),
 1095: 
 1096:         inbox_helper:enter_room(Room, Users),
 1097:         inbox_helper:make_members(Room, Alice, Users -- [Alice]),
 1098:         inbox_helper:leave_room(Kate, Room, Users),
 1099:         Stanza = escalus_stanza:set_id(
 1100:           escalus_stanza:groupchat_to(RoomAddr, Msg), Id),
 1101:         escalus:send(Bob, Stanza),
 1102:         inbox_helper:wait_for_groupchat_msg(Users -- [Kate]),
 1103:         inbox_helper:enter_room(Room, Kate, Users -- [Kate], 1),
 1104:         [AliceJid, BobJid, KateJid] = lists:map(fun inbox_helper:to_bare_lower/1, Users),
 1105:         BobRoomJid = muc_helper:room_address(Room, inbox_helper:nick(Bob)),
 1106:         %% Bob has 0 unread messages
 1107:         check_inbox(Bob, [#conv{unread = 0, from = BobRoomJid, to = BobJid, content = Msg}],
 1108:                     #{}, #{case_sensitive => true}),
 1109:         %% Alice and Kate have one conv with 1 unread message
 1110:         check_inbox(Alice, [#conv{unread = 1, from = BobRoomJid, to = AliceJid, content = Msg}],
 1111:                     #{}, #{case_sensitive => true}),
 1112:         check_inbox(Kate, [#conv{unread = 1, from = BobRoomJid, to = KateJid, content = Msg}],
 1113:                     #{}, #{case_sensitive => true})
 1114:     end).
 1115: 
 1116: unread_count_is_reset_after_sending_chatmarker(Config) ->
 1117:     escalus:fresh_story(Config, [{alice, 1}, {bob, 1}, {kate, 1}], fun(Alice, Bob, Kate) ->
 1118:         RoomConfig = muc_helper:start_room(Config, extract_user_specs(Alice), muc_helper:fresh_room_name(), <<"some_friendly_name">>, default),
 1119:         Users = [Alice, Bob, Kate],
 1120:         Msg = <<"Hi Room!">>,
 1121:         Id = <<"MyID">>,
 1122:         Room = ?config(room, RoomConfig),
 1123:         RoomAddr = muc_helper:room_address(Room),
 1124: 
 1125:         inbox_helper:enter_room(Room, Users),
 1126:         inbox_helper:make_members(Room, Alice, Users -- [Alice]),
 1127:         Stanza = escalus_stanza:set_id(
 1128:           escalus_stanza:groupchat_to(RoomAddr, Msg), Id),
 1129:         escalus:send(Bob, Stanza),
 1130:         inbox_helper:wait_for_groupchat_msg(Users),
 1131:         ChatMarkerWOType = escalus_stanza:chat_marker(RoomAddr, <<"displayed">>, Id),
 1132:         ChatMarker = escalus_stanza:setattr(ChatMarkerWOType, <<"type">>, <<"groupchat">>),
 1133:         %% User marks last message
 1134:         escalus:send(Kate, ChatMarker),
 1135:         inbox_helper:wait_for_groupchat_msg(Users),
 1136: 
 1137:         [AliceJid, BobJid, KateJid] = lists:map(fun inbox_helper:to_bare_lower/1, Users),
 1138:         BobRoomJid = muc_helper:room_address(Room, inbox_helper:nick(Bob)),
 1139:         %% Bob has 0 unread messages
 1140:         check_inbox(Bob, [#conv{unread = 0, from = BobRoomJid, to = BobJid, content = Msg}],
 1141:                     #{}, #{case_sensitive => true}),
 1142:         %% Alice have one conv with 1 unread message
 1143:         check_inbox(Alice, [#conv{unread = 1, from = BobRoomJid, to = AliceJid, content = Msg}],
 1144:                     #{}, #{case_sensitive => true}),
 1145:         %% Kate has 0 unread messages
 1146:         check_inbox(Kate, [#conv{unread = 0, from = BobRoomJid, to = KateJid, content = Msg}],
 1147:                     #{}, #{case_sensitive => true})
 1148:       end).
 1149: 
 1150: non_reset_marker_should_not_affect_muc_inbox(Config) ->
 1151:     escalus:fresh_story(Config, [{alice, 1}, {bob, 1}, {kate, 1}], fun(Alice, Bob, Kate) ->
 1152:         RoomConfig = muc_helper:start_room(Config, extract_user_specs(Alice), muc_helper:fresh_room_name(), <<"some_friendly_name">>, default),
 1153:         % %% GIVEN
 1154:         Users = [Alice, Bob, Kate],
 1155:         Msg = <<"Hi Room!">>,
 1156:         Id = <<"MyID">>,
 1157:         Room = ?config(room, RoomConfig),
 1158:         RoomAddr = muc_helper:room_address(Room),
 1159: 
 1160:         % %% WHEN
 1161:         inbox_helper:enter_room(Room, Users),
 1162:         inbox_helper:make_members(Room, Alice, Users -- [Alice]),
 1163:         Stanza = escalus_stanza:set_id(escalus_stanza:groupchat_to(RoomAddr, Msg), Id),
 1164:         escalus:send(Bob, Stanza),
 1165:         inbox_helper:wait_for_groupchat_msg(Users),
 1166:         % %% AND MARKED WRONG
 1167:         KateChatMarkerWOType = escalus_stanza:chat_marker(RoomAddr,<<"acknowledged">>, Id),
 1168:         KateChatMarker = escalus_stanza:setattr(KateChatMarkerWOType, <<"type">>, <<"groupchat">>),
 1169:         escalus:send(Kate, KateChatMarker),
 1170:         inbox_helper:wait_for_groupchat_msg(Users),
 1171: 
 1172:         [AliceJid, BobJid, KateJid] = lists:map(fun inbox_helper:to_bare_lower/1, Users),
 1173:         BobRoomJid = muc_helper:room_address(Room, inbox_helper:nick(Bob)),
 1174:         % %% THEN
 1175:         %% Bob has 0 unread messages
 1176:         check_inbox(Bob, [#conv{unread = 0, from = BobRoomJid, to = BobJid, content = Msg}],
 1177:                     #{}, #{case_sensitive => true}),
 1178:         %% Alice have one conv with 1 unread message
 1179:         check_inbox(Alice, [#conv{unread = 1, from = BobRoomJid, to = AliceJid, content = Msg}],
 1180:                     #{}, #{case_sensitive => true}),
 1181:         %% Kate has 1 unread messages
 1182:         check_inbox(Kate, [#conv{unread = 1, from = BobRoomJid, to = KateJid, content = Msg}],
 1183:                     #{}, #{case_sensitive => true})
 1184:       end).
 1185: 
 1186: unread_count_is_reset_after_sending_reset_stanza(Config) ->
 1187:     escalus:fresh_story(Config, [{alice, 1}, {bob, 1}, {kate, 1}], fun(Alice, Bob, Kate) ->
 1188:         RoomConfig = muc_helper:start_room(Config, extract_user_specs(Alice), muc_helper:fresh_room_name(), <<"some_friendly_name">>, default),
 1189:         % %% WITH
 1190:         Users = [Alice, Bob, Kate],
 1191:         Msg = <<"Hi Room!">>,
 1192:         Id = <<"MyID">>,
 1193:         Room = ?config(room, RoomConfig),
 1194:         RoomAddr = muc_helper:room_address(Room),
 1195:         % %% PROVIDED
 1196:         inbox_helper:enter_room(Room, Users),
 1197:         inbox_helper:make_members(Room, Alice, Users -- [Alice]),
 1198:         % %% WHEN A MESSAGE IS SENT
 1199:         Stanza = escalus_stanza:set_id(
 1200:           escalus_stanza:groupchat_to(RoomAddr, Msg), Id),
 1201:         escalus:send(Bob, Stanza),
 1202:         inbox_helper:wait_for_groupchat_msg(Users),
 1203:         [AliceJid, BobJid, KateJid] = lists:map(fun inbox_helper:to_bare_lower/1, Users),
 1204:         BobRoomJid = muc_helper:room_address(Room, inbox_helper:nick(Bob)),
 1205:         %% Make sure Kate gets her inbox updated
 1206:         check_inbox(Kate, [#conv{unread = 1, from = BobRoomJid, to = KateJid, content = Msg}],
 1207:                     #{}, #{case_sensitive => true}),
 1208:         % %% AND WHEN SEND RESET FOR ROOM
 1209:         inbox_helper:reset_inbox(Kate, RoomAddr),
 1210: 
 1211:         %% Bob has 0 unread messages
 1212:         check_inbox(Bob, [#conv{unread = 0, from = BobRoomJid, to = BobJid, content = Msg}],
 1213:                     #{}, #{case_sensitive => true}),
 1214:         %% Alice have one conv with 1 unread message
 1215:         check_inbox(Alice, [#conv{unread = 1, from = BobRoomJid, to = AliceJid, content = Msg}],
 1216:                     #{}, #{case_sensitive => true}),
 1217:         %% Kate has 0 unread messages
 1218:         check_inbox(Kate, [#conv{unread = 0, from = BobRoomJid, to = KateJid, content = Msg}],
 1219:                     #{}, #{case_sensitive => true}),
 1220:         inbox_helper:assert_has_no_stanzas([Alice, Bob, Kate])
 1221:       end).
 1222: 
 1223: private_messages_are_handled_as_one2one(Config) ->
 1224:     escalus:fresh_story(Config, [{alice, 1}, {bob, 1}, {kate, 1}], fun(Alice, Bob, Kate) ->
 1225:         RoomConfig = muc_helper:start_room(Config, extract_user_specs(Alice), muc_helper:fresh_room_name(), <<"some_friendly_name">>, default),
 1226:         Users = [Alice, Bob, Kate],
 1227:         Msg = <<"Hi Room!">>,
 1228:         Id = <<"MyID">>,
 1229:         Room = ?config(room, RoomConfig),
 1230:         BobRoomJid = muc_helper:room_address(Room, inbox_helper:nick(Bob)),
 1231:         KateRoomJid = muc_helper:room_address(Room, inbox_helper:nick(Kate)),
 1232: 
 1233:         inbox_helper:enter_room(Room, Users),
 1234:         inbox_helper:make_members(Room, Alice, Users -- [Alice]),
 1235:         Stanza = escalus_stanza:set_id(
 1236:           escalus_stanza:chat_to(BobRoomJid, Msg), Id),
 1237:         escalus:send(Kate, Stanza),
 1238:         _BobsPrivMsg = escalus:wait_for_stanza(Bob),
 1239: 
 1240:         KateJid = inbox_helper:to_bare_lower(Kate),
 1241:         %% Bob has 1 unread message
 1242:         check_inbox(Bob, [#conv{unread = 1, from = KateRoomJid, to = BobRoomJid, content = Msg}],
 1243:                     #{}, #{case_sensitive => true}),
 1244:         %% Alice gets nothing
 1245:         check_inbox(Alice, []),
 1246:         %% Kate has 1 conv with 0 unread messages
 1247:         check_inbox(Kate, [#conv{unread = 0, from = KateJid, to = BobRoomJid, content = Msg}],
 1248:                     #{}, #{case_sensitive => true, check_resource => false})
 1249:       end).
 1250: 
 1251: %%--------------------------------------------------------------------
 1252: %% Timestamp-related tests
 1253: %%--------------------------------------------------------------------
 1254: 
 1255: timestamp_is_updated_on_new_message(Config) ->
 1256:     escalus:fresh_story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) ->
 1257:          Body1 = <<"Hello Bob">>,
 1258:          Body2 = <<"Are you there?">>,
 1259:          %% We capture the timestamp after the first message
 1260:          escalus:send(Alice, escalus_stanza:chat_to(Bob, Body1)),
 1261:          _M1 = escalus:wait_for_stanza(Bob),
 1262:          #{respond_messages := [Item1]}
 1263:              = check_inbox(Bob, [#conv{unread = 1, from = Alice, to = Bob, content = Body1}]),
 1264:          TStamp1 = inbox_helper:timestamp_from_item(Item1),
 1265:          %% We capture the timestamp after the second message
 1266:          escalus:send(Alice, escalus_stanza:chat_to(Bob, Body2)),
 1267:          _M2 = escalus:wait_for_stanza(Bob),
 1268:          #{respond_messages := [Item2]}
 1269:              = check_inbox(Bob, [#conv{unread = 2, from = Alice, to = Bob, content = Body2}]),
 1270:          TStamp2 = inbox_helper:timestamp_from_item(Item2),
 1271:          %% Timestamp after second message must be higher
 1272:          case TStamp2 > TStamp1 of
 1273:              true -> ok;
 1274:              false -> error(#{ type => timestamp_is_not_greater,
 1275:                                item1 => Item1,
 1276:                                item2 => Item2,
 1277:                                tstamp1 => TStamp1,
 1278:                                tstamp2 => TStamp2 })
 1279:          end
 1280:   end).
 1281: 
 1282: order_by_timestamp_ascending(Config) ->
 1283:     escalus:fresh_story(Config, [{alice, 1}, {bob, 1}, {kate, 1}], fun(Alice, Bob, Kate) ->
 1284:         #{ Alice := AliceConvs, Bob := BobConvs, Kate := KateConvs } =
 1285:         given_conversations_between(Alice, [Bob, Kate]),
 1286: 
 1287:         %% Alice has two conversations in her inbox (no unread messages)
 1288:         %% check_inbox will reverse conversation list automatically
 1289:         check_inbox(Alice, AliceConvs, #{ order => asc }, #{}),
 1290: 
 1291:         %% Kate has one conversation with one unread
 1292:         check_inbox(Kate, KateConvs),
 1293: 
 1294:         %% Bob has one conversation with one unread
 1295:         check_inbox(Bob, BobConvs)
 1296:       end).
 1297: 
 1298: get_by_timestamp_range(Config) ->
 1299:     escalus:fresh_story(Config, [{alice, 1}, {bob, 1}, {kate, 1}], fun(Alice, Bob, Kate) ->
 1300:         #{ Alice := AliceConvs, time_before := TimeBefore } =
 1301:         given_conversations_between(Alice, [Bob, Kate]),
 1302: 
 1303:         ConvWithBob = lists:keyfind(Bob, #conv.to, AliceConvs),
 1304:         TimeAfterBob = ConvWithBob#conv.time_after,
 1305: 
 1306:         %% Between given timestamps we have only one conversation: with Bob
 1307:         check_inbox(Alice, [ConvWithBob], #{ start => TimeBefore, 'end' => TimeAfterBob }, #{})
 1308:     end).
 1309: 
 1310: get_with_start_timestamp(Config) ->
 1311:     escalus:fresh_story(Config, [{alice, 1}, {bob, 1}, {kate, 1}], fun(Alice, Bob, Kate) ->
 1312:         #{ Alice := AliceConvs } =
 1313:         given_conversations_between(Alice, [Bob, Kate]),
 1314: 
 1315:         ConvWithBob = lists:keyfind(Bob, #conv.to, AliceConvs),
 1316:         TimeAfterBob = ConvWithBob#conv.time_after,
 1317:         ConvWithKate = lists:keyfind(Kate, #conv.to, AliceConvs),
 1318: 
 1319:         %% After given timestamp we have only one conversation: with Kate
 1320:         check_inbox(Alice, [ConvWithKate], #{ start => TimeAfterBob }, #{})
 1321:     end).
 1322: 
 1323: get_with_end_timestamp(Config) ->
 1324:     escalus:fresh_story(Config, [{alice, 1}, {bob, 1}, {kate, 1}], fun(Alice, Bob, Kate) ->
 1325:         #{ Alice := AliceConvs } =
 1326:         given_conversations_between(Alice, [Bob, Kate]),
 1327: 
 1328:         ConvWithBob = lists:keyfind(Bob, #conv.to, AliceConvs),
 1329:         TimeAfterBob = ConvWithBob#conv.time_after,
 1330:         %% Before given timestamp we have only one conversation: with Bob
 1331:         %% TODO: Improve this test to store 3+ conversations in Alice's inbox
 1332:         check_inbox(Alice, [ConvWithBob], #{ 'end' => TimeAfterBob }, #{})
 1333:     end).
 1334: 
 1335: 
 1336: %% Bin flushes tests
 1337: bin_is_not_included_by_default(Config) ->
 1338:     escalus:fresh_story(Config, [{alice, 1}, {bob, 1}, {kate, 1}], fun(Alice, Bob, Kate) ->
 1339:         RoomName = create_room_and_make_users_leave(Alice, Bob, Kate),
 1340:         RoomJid = room_bin_jid(RoomName),
 1341:         BobJid = inbox_helper:to_bare_lower(Bob),
 1342:         Convs = [#conv{unread = 1, from = RoomJid, to = BobJid,
 1343:                        verify = fun inbox_helper:verify_is_none_aff_change/2}],
 1344:         %% Fetching all does include it
 1345:         check_inbox(Bob, Convs, #{box => all}),
 1346:         %% Fetching without explicit box name skips the bin
 1347:         check_inbox(Bob, [], #{})
 1348:     end).
 1349: 
 1350: rest_api_bin_flush_user(Config) ->
 1351:     escalus:fresh_story(Config, [{alice, 1}, {bob, 1}, {kate, 1}], fun(Alice, Bob, Kate) ->
 1352:         create_room_and_make_users_leave(Alice, Bob, Kate),
 1353:         %% It is not in his bin anymore after triggering a bin flush
 1354:         BobName = escalus_utils:get_username(Bob),
 1355:         BobDomain = escalus_utils:get_server(Bob),
 1356:         Path = <<"/inbox", "/", (BobDomain)/binary, "/", (BobName)/binary, "/0/bin">>,
 1357:         {{<<"200">>, <<"OK">>}, NumOfRows} = rest_helper:delete(admin, Path),
 1358:         ?assertEqual(1, NumOfRows),
 1359:         check_inbox(Bob, [], #{box => bin})
 1360:     end).
 1361: 
 1362: rest_api_bin_flush_user_errors(Config) ->
 1363:     Config1 = escalus_fresh:create_users(Config, [{alice, 1}]),
 1364:     User = escalus_users:get_username(Config1, alice),
 1365:     Domain = escalus_users:get_server(Config1, alice),
 1366:     {{<<"400">>, <<"Bad Request">>}, <<"Invalid number of days">>} =
 1367:         rest_helper:delete(admin, <<"/inbox/", Domain/binary, "/", User/binary, "/x/bin">>),
 1368:     {{<<"400">>, <<"Bad Request">>}, <<"Invalid JID">>} =
 1369:         rest_helper:delete(admin, <<"/inbox/", Domain/binary, "/@/0/bin">>),
 1370:     {{<<"404">>, <<"Not Found">>}, <<"Domain not found">>} =
 1371:         rest_helper:delete(admin, <<"/inbox/baddomain/", User/binary, "/0/bin">>),
 1372:     {{<<"404">>, <<"Not Found">>}, <<"User baduser@", _/binary>>} =
 1373:         rest_helper:delete(admin, <<"/inbox/", Domain/binary, "/baduser/0/bin">>).
 1374: 
 1375: rest_api_bin_flush_all(Config) ->
 1376:     escalus:fresh_story(Config, [{alice, 1}, {bob, 1}, {kate, 1}], fun(Alice, Bob, Kate) ->
 1377:         create_room_and_make_users_leave(Alice, Bob, Kate),
 1378:         %% It is not in any bin anymore after triggering a bin flush
 1379:         HostTypePath = uri_string:normalize(#{path => domain_helper:host_type()}),
 1380:         Path = <<"/inbox/", HostTypePath/binary, "/0/bin">>,
 1381:         {{<<"200">>, <<"OK">>}, NumOfRows} = rest_helper:delete(admin, Path),
 1382:         ?assertEqual(2, NumOfRows),
 1383:         check_inbox(Bob, [], #{box => bin}),
 1384:         check_inbox(Kate, [], #{box => bin})
 1385:     end).
 1386: 
 1387: rest_api_bin_flush_all_errors(_Config) ->
 1388:     HostTypePath = uri_string:normalize(#{path => domain_helper:host_type()}),
 1389:     {{<<"400">>, <<"Bad Request">>}, <<"Invalid number of days">>} =
 1390:         rest_helper:delete(admin, <<"/inbox/", HostTypePath/binary, "/x/bin">>),
 1391:     {{<<"404">>, <<"Not Found">>}, <<"Host type not found">>} =
 1392:         rest_helper:delete(admin, <<"/inbox/bad_host_type/0/bin">>).
 1393: 
 1394: timeout_cleaner_flush_all(Config) ->
 1395:     escalus:fresh_story(Config, [{alice, 1}, {bob, 1}, {kate, 1}], fun(Alice, Bob, Kate) ->
 1396:         create_room_and_make_users_leave(Alice, Bob, Kate),
 1397:         %% It is eventually not in any bin thanks to the periodic cleanouts
 1398:         check_inbox(Bob, [], #{box => bin}),
 1399:         check_inbox(Kate, [], #{box => bin})
 1400:     end).
 1401: 
 1402: xmpp_bin_flush(Config) ->
 1403:     escalus:fresh_story(Config, [{alice, 1}, {bob, 1}, {kate, 1}], fun(Alice, Bob, Kate) ->
 1404:         create_room_and_make_users_leave(Alice, Bob, Kate),
 1405:         %% It is eventually not in any bin thanks to the periodic cleanouts
 1406:         %% Bob requests flush through xmpp
 1407:         Iq = escalus_stanza:iq(<<"set">>,
 1408:                                [#xmlel{name = <<"empty-bin">>,
 1409:                                        attrs = [{<<"xmlns">>, inbox_helper:inbox_ns()}],
 1410:                                        children = []}]),
 1411:         escalus:send(Bob, Iq),
 1412:         escalus:assert(is_iq_result, [Iq], escalus:wait_for_stanza(Bob)),
 1413:         check_inbox(Bob, [], #{box => bin})
 1414:     end).
 1415: 
 1416: 
 1417: %% helpers
 1418: create_room_and_make_users_leave(Alice, Bob, Kate) ->
 1419:     RoomName = pubsub_tools:pubsub_node_name(),
 1420:     inbox_helper:create_room_and_check_inbox(Alice, [Bob, Kate], RoomName),
 1421:     %% Bob leaves the room
 1422:     muc_light_helper:user_leave(RoomName, Bob, [{Alice, owner}, {Kate, member}]),
 1423:     muc_light_helper:user_leave(RoomName, Kate, [{Alice, owner}]),
 1424:     %% Bob doesn't have conversation in his inbox, nor Kate
 1425:     check_inbox(Bob, [], #{box => inbox}),
 1426:     check_inbox(Kate, [], #{box => inbox}),
 1427:     RoomName.
 1428: 
 1429: if_async_check_bin(Config, Bob, Convs) ->
 1430:     case maps:get(backend, ?config(inbox_opts, Config), rdbms) of
 1431:         rdbms -> ok;
 1432:         rdbms_async ->
 1433:             check_inbox(Bob, Convs, #{box => bin})
 1434:     end.
 1435: 
 1436: start_hook_listener() ->
 1437:     TestCasePid = self(),
 1438:     distributed_helper:rpc(distributed_helper:mim(), ?MODULE, rpc_start_hook_handler, [TestCasePid, domain_helper:host_type()]).
 1439: 
 1440: stop_hook_listener(HookExtra) ->
 1441:     distributed_helper:rpc(distributed_helper:mim(), ?MODULE, rpc_stop_hook_handler, [HookExtra, domain_helper:host_type()]).
 1442: 
 1443: rpc_start_hook_handler(TestCasePid, HostType) ->
 1444:     Extra = #{test_case_pid => TestCasePid},
 1445:     gen_hook:add_handler(does_user_exist, HostType, fun ?MODULE:hook_handler_fn/3, Extra, 1),
 1446:     Extra.
 1447: 
 1448: rpc_stop_hook_handler(HookExtra, HostType) ->
 1449:     gen_hook:delete_handler(does_user_exist, HostType, fun ?MODULE:hook_handler_fn/3, HookExtra, 1).
 1450: 
 1451: hook_handler_fn(Acc,
 1452:                 #{jid := User} = _Params,
 1453:                 #{test_case_pid := Pid} = _Extra) ->
 1454:     Pid ! {input, User#jid.luser},
 1455:     {ok, Acc}.
 1456: 
 1457: verify_hook_listener(RoomName) ->
 1458:     receive
 1459:         {input, RoomName} ->
 1460:             ct:fail("does_user_exist was called with a room jid");
 1461:         {input, _} ->
 1462:             verify_hook_listener(RoomName)
 1463:     after 100 ->
 1464:               ct:pal("OK")
 1465:     end.