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