1: -module(rest_client_SUITE).
    2: -compile([export_all, nowarn_export_all]).
    3: 
    4: -include_lib("escalus/include/escalus.hrl").
    5: -include_lib("eunit/include/eunit.hrl").
    6: -include_lib("eunit/include/eunit.hrl").
    7: -include_lib("common_test/include/ct.hrl").
    8: 
    9: -import(rest_helper,
   10:         [decode_maplist/1,
   11:          gett/3,
   12:          post/4,
   13:          putt/4,
   14:          delete/3,
   15:          delete/4]
   16:          ).
   17: 
   18: -import(domain_helper, [host_type/0]).
   19: 
   20: -define(PRT(X, Y), ct:pal("~p: ~p", [X, Y])).
   21: -define(OK, {<<"200">>, <<"OK">>}).
   22: -define(CREATED, {<<"201">>, <<"Created">>}).
   23: -define(NOCONTENT, {<<"204">>, <<"No Content">>}).
   24: -define(ERROR, {<<"500">>, _}).
   25: -define(NOT_FOUND, {<<"404">>, _}).
   26: -define(NOT_IMPLEMENTED, {<<"501">>, _}).
   27: -define(UNAUTHORIZED, {<<"401">>, <<"Unauthorized">>}).
   28: 
   29: %% --------------------------------------------------------------------
   30: %% Common Test stuff
   31: %% --------------------------------------------------------------------
   32: 
   33: all() ->
   34:     [{group, messages},
   35:      {group, muc},
   36:      {group, muc_config},
   37:      {group, roster},
   38:      {group, messages_with_props},
   39:      {group, security}].
   40: 
   41: groups() ->
   42:     [{messages_with_props, [parallel], message_with_props_test_cases()},
   43:      {messages_with_thread, [parallel], message_with_thread_test_cases()},
   44:      {messages, [parallel], message_test_cases()},
   45:      {muc, [pararell], muc_test_cases()},
   46:      {muc_config, [], muc_config_cases()},
   47:      {roster, [parallel], roster_test_cases()},
   48:      {security, [], security_test_cases()}].
   49: 
   50: message_test_cases() ->
   51:     [msg_is_sent_and_delivered_over_xmpp,
   52:      msg_is_sent_and_delivered_over_sse,
   53:      all_messages_are_archived,
   54:      messages_with_user_are_archived,
   55:      messages_can_be_paginated].
   56: 
   57: muc_test_cases() ->
   58:      [room_is_created,
   59:       room_is_created_with_given_identifier,
   60:       user_is_invited_to_a_room,
   61:       user_is_removed_from_a_room,
   62:       rooms_can_be_listed,
   63:       owner_can_leave_a_room_and_auto_select_owner,
   64:       user_can_leave_a_room,
   65:       invitation_to_room_is_forbidden_for_non_memeber,
   66:       msg_is_sent_and_delivered_in_room,
   67:       sending_message_with_wrong_body_results_in_bad_request,
   68:       sending_message_with_no_body_results_in_bad_request,
   69:       sending_markable_message_with_no_body_results_in_bad_request,
   70:       sending_message_not_in_JSON_results_in_bad_request,
   71:       sending_message_by_not_room_member_results_in_forbidden,
   72:       messages_are_archived_in_room,
   73:       chat_markers_are_archived_in_room,
   74:       markable_property_is_archived_in_room,
   75:       only_room_participant_can_read_messages,
   76:       messages_can_be_paginated_in_room,
   77:       room_msg_is_sent_and_delivered_over_sse,
   78:       aff_change_msg_is_delivered_over_sse,
   79:       room_is_created_with_given_jid,
   80:       room_is_not_created_with_jid_not_matching_hostname,
   81:       room_can_be_fetched_by_jid,
   82:       messages_can_be_sent_and_fetched_by_room_jid,
   83:       user_can_be_added_and_removed_by_room_jid
   84:      ].
   85: 
   86: muc_config_cases() ->
   87:     [
   88:       config_can_be_changed_by_owner,
   89:       config_cannot_be_changed_by_member,
   90:       config_can_be_changed_by_all
   91:     ].
   92: 
   93: roster_test_cases() ->
   94:     [add_contact_and_invite,
   95:      add_contact_and_be_invited,
   96:      add_and_remove,
   97:      add_and_remove_some_contacts_properly,
   98:      add_and_remove_some_contacts_with_nonexisting,
   99:      break_stuff].
  100: 
  101: message_with_props_test_cases() ->
  102:     [
  103:      msg_with_props_is_sent_and_delivered_over_xmpp,
  104:      msg_with_props_can_be_parsed,
  105:      msg_with_malformed_props_can_be_parsed,
  106:      msg_with_malformed_props_is_sent_and_delivered_over_xmpp
  107:      ].
  108: 
  109: message_with_thread_test_cases() ->
  110:     [msg_with_thread_is_sent_and_delivered_over_xmpp,
  111:      msg_with_thread_can_be_parsed,
  112:      msg_with_thread_and_parent_is_sent_and_delivered_over_xmpp,
  113:      msg_with_thread_and_parent_can_be_parse,
  114:      msg_without_thread_can_be_parsed,
  115:      msg_without_thread_is_sent_and_delivered_over_xmpp].
  116: 
  117: security_test_cases() ->
  118:     [
  119:      default_http_server_name_is_returned_if_not_changed,
  120:      non_default_http_server_name_is_returned_if_configured
  121:     ].
  122: 
  123: init_per_suite(Config) ->
  124:     Config1 = init_modules(Config),
  125:     [{muc_light_host, muc_light_helper:muc_host()}
  126:      | escalus:init_per_suite(Config1)].
  127: 
  128: end_per_suite(Config) ->
  129:     escalus_fresh:clean(),
  130:     dynamic_modules:restore_modules(Config),
  131:     escalus:end_per_suite(Config).
  132: 
  133: init_modules(Config) ->
  134:     HostType = host_type(),
  135:     Config1 = dynamic_modules:save_modules(HostType, Config),
  136:     Config2 = rest_helper:maybe_enable_mam(mam_helper:backend(), HostType, Config1),
  137:     dynamic_modules:ensure_modules(HostType, required_modules(suite)),
  138:     Config2.
  139: 
  140: init_per_group(_GN, C) ->
  141:     C.
  142: 
  143: end_per_group(_GN, C) ->
  144:     C.
  145: 
  146: init_per_testcase(config_can_be_changed_by_all = TC, Config) ->
  147:     HostType = host_type(),
  148:     DefaultConfig = dynamic_modules:save_modules(HostType, Config),
  149:     dynamic_modules:ensure_modules(HostType, required_modules(TC)),
  150:     escalus:init_per_testcase(config_can_be_changed_by_all, DefaultConfig);
  151: init_per_testcase(TC, Config) ->
  152:     MAMTestCases = [all_messages_are_archived,
  153:                     messages_with_user_are_archived,
  154:                     messages_can_be_paginated,
  155:                     messages_are_archived_in_room,
  156:                     chat_markers_are_archived_in_room,
  157:                     markable_property_is_archived_in_room,
  158:                     only_room_participant_can_read_messages,
  159:                     messages_can_be_paginated_in_room,
  160:                     messages_can_be_sent_and_fetched_by_room_jid,
  161:                     msg_with_props_is_sent_and_delivered_over_xmpp,
  162:                     msg_with_props_can_be_parsed,
  163:                     msg_with_malformed_props_can_be_parsed,
  164:                     msg_with_malformed_props_is_sent_and_delivered_over_xmpp,
  165:                     msg_with_thread_is_sent_and_delivered_over_xmpp,
  166:                     msg_with_thread_can_be_parse,
  167:                     msg_with_thread_and_parent_is_sent_and_delivered_over_xmpp,
  168:                     msg_with_thread_and_parent_can_be_parse,
  169:                     msg_without_thread_can_be_parsed,
  170:                     msg_without_thread_is_sent_and_delivered_over_xmpp
  171:                    ],
  172:     rest_helper:maybe_skip_mam_test_cases(TC, MAMTestCases, Config).
  173: 
  174: end_per_testcase(config_can_be_changed_by_all, Config) ->
  175:     dynamic_modules:restore_modules(Config),
  176:     escalus:end_per_testcase(config_can_be_changed_by_all, Config);
  177: end_per_testcase(TC, C) ->
  178:     escalus:end_per_testcase(TC, C).
  179: 
  180: %% Module configuration - set up per suite and for special test cases
  181: %% TODO: include MAM configuration here
  182: 
  183: required_modules(SuiteOrTC) ->
  184:     [{mod_muc_light, common_muc_light_opts() ++ muc_light_opts(SuiteOrTC)}].
  185: 
  186: muc_light_opts(config_can_be_changed_by_all) ->
  187:     [{all_can_configure, true}];
  188: muc_light_opts(suite) ->
  189:     [].
  190: 
  191: common_muc_light_opts() ->
  192:     MucPattern = distributed_helper:subhost_pattern(muc_light_helper:muc_host_pattern()),
  193:     [{host, MucPattern},
  194:      {rooms_in_rosters, true}].
  195: 
  196: %% --------------------------------------------------------------------
  197: %% Test cases
  198: %% --------------------------------------------------------------------
  199: 
  200: msg_is_sent_and_delivered_over_xmpp(Config) ->
  201:     escalus:fresh_story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) ->
  202:         M = send_message(alice, Alice, Bob),
  203:         Msg = escalus:wait_for_stanza(Bob),
  204:         escalus:assert(is_chat_message, [maps:get(body, M)], Msg)
  205:     end).
  206: 
  207: msg_is_sent_and_delivered_over_sse(ConfigIn) ->
  208:     Config = escalus_fresh:create_users(ConfigIn, [{alice, 1}, {bob, 1}]),
  209:     Bob = escalus_users:get_userspec(Config, bob),
  210:     Alice = escalus_users:get_userspec(Config, alice),
  211: 
  212:     Conn = connect_to_sse({alice, Alice}),
  213:     M = send_message(bob, Bob, Alice),
  214: 
  215:     Event = wait_for_event(Conn),
  216:     Data = jiffy:decode(maps:get(data, Event), [return_maps]),
  217: 
  218:     assert_json_message(M, Data),
  219: 
  220:     stop_sse(Conn).
  221: 
  222: room_msg_is_sent_and_delivered_over_sse(ConfigIn) ->
  223:     Config = escalus_fresh:create_users(ConfigIn, [{alice, 1}, {bob, 1}]),
  224:     Bob = escalus_users:get_userspec(Config, bob),
  225:     Alice = escalus_users:get_userspec(Config, alice),
  226:     RoomID = given_new_room_with_users({alice, Alice}, [{bob, Bob}]),
  227:     RoomInfo = get_room_info({alice, Alice}, RoomID),
  228:     true = is_participant(Bob, <<"member">>, RoomInfo),
  229:     Conn = connect_to_sse({bob, Bob}),
  230:     Message = given_message_sent_to_room(RoomID, {alice, Alice}),
  231:     Event = wait_for_event(Conn),
  232:     Data = jiffy:decode(maps:get(data, Event), [return_maps]),
  233:     assert_json_room_sse_message(Message#{room => RoomID, type => <<"message">>},
  234:                                  Data),
  235:     stop_sse(Conn).
  236: 
  237: aff_change_msg_is_delivered_over_sse(ConfigIn) ->
  238:     Config = escalus_fresh:create_users(ConfigIn, [{alice, 1}, {bob, 1}]),
  239:     Bob = escalus_users:get_userspec(Config, bob),
  240:     Alice = escalus_users:get_userspec(Config, alice),
  241:     RoomID = given_new_room({alice, Alice}),
  242:     Conn = connect_to_sse({bob, Bob}),
  243:     given_user_invited({alice, Alice}, RoomID, Bob),
  244:     Event = wait_for_event(Conn),
  245:     Data = jiffy:decode(maps:get(data, Event), [return_maps]),
  246:     BobJID = user_jid(Bob),
  247:     RoomJID = room_jid(RoomID, Config),
  248:     assert_json_room_sse_message(#{room => RoomID,
  249:                                    from => RoomJID,
  250:                                    type => <<"affiliation">>,
  251:                                    user => BobJID},
  252:                                  Data),
  253:     stop_sse(Conn).
  254: 
  255: all_messages_are_archived(Config) ->
  256:     escalus:fresh_story(Config, [{alice, 1}, {bob, 1}, {kate, 1}], fun(Alice, Bob, Kate) ->
  257:         Sent = [M1 | _] = send_messages(Config, Alice, Bob, Kate),
  258:         AliceJID = maps:get(to, M1),
  259:         AliceCreds = {AliceJID, user_password(alice)},
  260:         GetPath = lists:flatten("/messages/"),
  261:         {{<<"200">>, <<"OK">>}, Msgs} = rest_helper:gett(client, GetPath, AliceCreds),
  262:         Received = [_Msg1, _Msg2, _Msg3] = rest_helper:decode_maplist(Msgs),
  263:         assert_messages(Sent, Received)
  264: 
  265:     end).
  266: 
  267: messages_with_user_are_archived(Config) ->
  268:     escalus:fresh_story(Config, [{alice, 1}, {bob, 1}, {kate, 1}], fun(Alice, Bob, Kate) ->
  269:         [M1, _M2, M3] = send_messages(Config, Alice, Bob, Kate),
  270:         AliceJID = maps:get(to, M1),
  271:         KateJID = escalus_utils:jid_to_lower(escalus_client:short_jid(Kate)),
  272:         AliceCreds = {AliceJID, user_password(alice)},
  273:         GetPath = lists:flatten(["/messages/", binary_to_list(KateJID)]),
  274:         {{<<"200">>, <<"OK">>}, Msgs} = rest_helper:gett(client, GetPath, AliceCreds),
  275:         Recv = [_Msg2] = rest_helper:decode_maplist(Msgs),
  276:         assert_messages([M3], Recv)
  277: 
  278:     end).
  279: 
  280: messages_can_be_paginated(Config) ->
  281:     escalus:fresh_story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) ->
  282:         AliceJID = escalus_utils:jid_to_lower(escalus_client:short_jid(Alice)),
  283:         BobJID = escalus_utils:jid_to_lower(escalus_client:short_jid(Bob)),
  284:         rest_helper:fill_archive(Alice, Bob),
  285:         mam_helper:maybe_wait_for_archive(Config),
  286:         AliceCreds = {AliceJID, user_password(alice)},
  287:         % recent msgs with a limit
  288:         M1 = get_messages(AliceCreds, BobJID, 10),
  289:         6 = length(M1),
  290:         M2 = get_messages(AliceCreds, BobJID, 3),
  291:         3 = length(M2),
  292:         % older messages - earlier then the previous midnight
  293:         PriorTo = rest_helper:make_timestamp(-1, {0, 0, 1}),
  294:         M3 = get_messages(AliceCreds, BobJID, PriorTo, 10),
  295:         4 = length(M3),
  296:         [Oldest|_] = M3,
  297:         <<"A">> = maps:get(body, Oldest),
  298:         % same with limit
  299:         M4 = get_messages(AliceCreds, BobJID, PriorTo, 2),
  300:         2 = length(M4),
  301:         [Oldest2|_] = M4,
  302:         <<"B">> = maps:get(body, Oldest2)
  303:     end).
  304: 
  305: room_is_created(Config) ->
  306:     escalus:fresh_story(Config, [{alice, 1}], fun(Alice) ->
  307:         RoomID = given_new_room({alice, Alice}),
  308:         RoomInfo = get_room_info({alice, Alice}, RoomID),
  309:         assert_room_info(Alice, RoomInfo)
  310:     end).
  311: 
  312: room_is_created_with_given_identifier(Config) ->
  313:     escalus:fresh_story(Config, [{alice, 1}], fun(Alice) ->
  314:         GivenRoomID = muc_helper:fresh_room_name(),
  315:         GivenRoomID = given_new_room({alice, Alice}, GivenRoomID),
  316:         RoomInfo = get_room_info({alice, Alice}, GivenRoomID),
  317:         assert_room_info(Alice, RoomInfo)
  318:     end).
  319: 
  320: config_can_be_changed_by_owner(Config) ->
  321:     escalus:fresh_story(Config, [{alice, 1}], fun(Alice) ->
  322:         RoomID = muc_helper:fresh_room_name(),
  323:         RoomJID = room_jid(RoomID, Config),
  324:         RoomID = given_new_room({alice, Alice}, RoomJID, <<"old_name">>),
  325:         RoomInfo = get_room_info({alice, Alice}, RoomID),
  326:         assert_property_value(<<"name">>, <<"old_name">>, RoomInfo),
  327: 
  328:         {{<<"204">>,<<"No Content">>},<<>>} =
  329:             when_config_change({alice, Alice}, RoomJID, <<"new_name">>, <<"new_subject">>),
  330:         NewRoomInfo = get_room_info({alice, Alice}, RoomID),
  331:         assert_property_value(<<"name">>, <<"new_name">>, NewRoomInfo),
  332:         assert_property_value(<<"subject">>, <<"new_subject">>, NewRoomInfo)
  333:     end).
  334: 
  335: config_cannot_be_changed_by_member(Config) ->
  336:     escalus:fresh_story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) ->
  337:         RoomID = given_new_room_with_users({alice, Alice}, [{bob, Bob}]),
  338:         RoomJID = room_jid(RoomID, Config),
  339:         {{<<"403">>,<<"Forbidden">>},<<>>} =
  340:             when_config_change({bob, Bob}, RoomJID, <<"other_name">>, <<"other_subject">>),
  341:         NewRoomInfo = get_room_info({bob, Bob}, RoomID),
  342:         assert_property_value(<<"name">>,<<"new_room_name">>, NewRoomInfo)
  343:     end).
  344: 
  345: config_can_be_changed_by_all(Config) ->
  346:     escalus:fresh_story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) ->
  347:         RoomID = given_new_room_with_users({alice, Alice}, [{bob, Bob}]),
  348:         RoomJID = room_jid(RoomID, Config),
  349:         RoomInfo = get_room_info({alice, Alice}, RoomID),
  350:         assert_property_value(<<"name">>,<<"new_room_name">>,RoomInfo),
  351:         {{<<"204">>,<<"No Content">>},<<>>} =
  352:             when_config_change({bob, Bob}, RoomJID, <<"other_name">>, <<"other_subject">>),
  353:         NewRoomInfo = get_room_info({alice, Alice}, RoomID),
  354:         assert_property_value(<<"name">>,<<"other_name">>,NewRoomInfo)
  355:     end).
  356: 
  357: rooms_can_be_listed(Config) ->
  358:     escalus:fresh_story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) ->
  359:         [] = get_my_rooms({alice, Alice}),
  360:         RoomID = given_new_room_with_users({alice, Alice}, [{bob, Bob}]),
  361:         [{Room}] = get_my_rooms({alice, Alice}),
  362:         RoomMap = maps:from_list(Room),
  363:         RoomID = maps:get(<<"id">>, RoomMap),
  364:         true = maps:is_key(<<"name">>, RoomMap),
  365:         true = maps:is_key(<<"subject">>, RoomMap),
  366:         [{Room}] = get_my_rooms({bob, Bob})
  367:     end).
  368: 
  369: user_is_invited_to_a_room(Config) ->
  370:     escalus:fresh_story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) ->
  371:         RoomID = given_new_room_with_users({alice, Alice}, [{bob, Bob}]),
  372:         RoomInfo = get_room_info({alice, Alice}, RoomID),
  373:         true = is_participant(Bob, <<"member">>, RoomInfo),
  374:         IQ = escalus_stanza:iq_get(<<"urn:xmpp:muclight:0#affiliations">>, []),
  375:         RoomJID = room_jid(RoomID, Config),
  376:         escalus:send(Alice, escalus_stanza:to(IQ, RoomJID)),
  377:         escalus:assert(is_iq_result, [IQ], escalus:wait_for_stanza(Alice))
  378: 
  379:     end).
  380: 
  381: user_is_removed_from_a_room(Config) ->
  382:     escalus:fresh_story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) ->
  383:         RoomID = given_new_room_with_users({alice, Alice}, [{bob, Bob}]),
  384:         {{<<"204">>, _}, _} = remove_user_from_a_room({alice, Alice}, RoomID, Bob),
  385:         Stanza = escalus:wait_for_stanza(Bob),
  386:         assert_aff_change_stanza(Stanza, Bob, <<"none">>)
  387:     end).
  388: 
  389: owner_can_leave_a_room_and_auto_select_owner(Config) ->
  390:     escalus:fresh_story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) ->
  391:         RoomID = given_new_room_with_users({alice, Alice}, [{bob, Bob}]),
  392:         {{<<"204">>, _}, _} = remove_user_from_a_room({alice, Alice}, RoomID, Alice),
  393:         Stanza = escalus:wait_for_stanza(Bob),
  394:         assert_aff_change_stanza(Stanza, Alice, <<"none">>),
  395:         assert_aff_change_stanza(Stanza, Bob, <<"owner">>)
  396:     end).
  397: 
  398: user_can_leave_a_room(Config) ->
  399:     escalus:fresh_story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) ->
  400:         RoomID = given_new_room_with_users({alice, Alice}, [{bob, Bob}]),
  401:         {{<<"204">>, _}, _} = remove_user_from_a_room({bob, Bob}, RoomID, Bob),
  402:         Stanza = escalus:wait_for_stanza(Bob),
  403:         assert_aff_change_stanza(Stanza, Bob, <<"none">>)
  404:     end).
  405: 
  406: invitation_to_room_is_forbidden_for_non_memeber(Config) ->
  407:     escalus:fresh_story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) ->
  408:         RoomID = given_new_room({alice, Alice}),
  409:         {{<<"403">>, <<"Forbidden">>}, _ } = invite_to_room({bob, Bob}, RoomID,
  410:                                                             <<"auser@domain.com">>)
  411:     end).
  412: 
  413: msg_is_sent_and_delivered_in_room(Config) ->
  414:     escalus:fresh_story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) ->
  415:         given_new_room_with_users_and_msgs({alice, Alice}, [{bob, Bob}])
  416:     end).
  417: 
  418: 
  419: sending_message_by_not_room_member_results_in_forbidden(Config) ->
  420:     escalus:fresh_story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) ->
  421:         Sender = {alice, Alice},
  422:         RoomID = given_new_room_with_users(Sender, []),
  423:         Result = given_message_sent_to_room(RoomID, {bob, Bob}, #{body => <<"Hello, I'm not member">>}),
  424:         ?assertMatch({{<<"403">>, <<"Forbidden">>}, _}, Result)
  425: 
  426:     end).
  427: 
  428: sending_message_with_wrong_body_results_in_bad_request(Config) ->
  429:     escalus:fresh_story(Config, [{alice, 1}], fun(Alice) ->
  430:         Sender = {alice, Alice},
  431:         RoomID = given_new_room_with_users(Sender, []),
  432:         Result = given_message_sent_to_room(RoomID, Sender, #{body => #{nested => <<"structure">>}}),
  433:         ?assertMatch({{<<"400">>, <<"Bad Request">>}, <<"Invalid body, it must be a string">>}, Result)
  434:     end).
  435: 
  436: sending_message_with_no_body_results_in_bad_request(Config) ->
  437:     escalus:fresh_story(Config, [{alice, 1}], fun(Alice) ->
  438:         Sender = {alice, Alice},
  439:         RoomID = given_new_room_with_users(Sender, []),
  440:         Result = given_message_sent_to_room(RoomID, Sender, #{no_body => <<"This should be in body element">>}),
  441:         ?assertMatch({{<<"400">>, <<"Bad Request">>}, <<"No valid message elements">>}, Result)
  442:     end).
  443: 
  444: sending_markable_message_with_no_body_results_in_bad_request(Config) ->
  445:     escalus:fresh_story(Config, [{alice, 1}], fun(Alice) ->
  446:         Sender = {alice, Alice},
  447:         RoomID = given_new_room_with_users(Sender, []),
  448:         Result = given_message_sent_to_room(RoomID, Sender, #{markable => true}),
  449:         ?assertMatch({{<<"400">>, <<"Bad Request">>}, <<"No valid message elements">>}, Result)
  450:     end).
  451: 
  452: sending_message_not_in_JSON_results_in_bad_request(Config) ->
  453:     escalus:fresh_story(Config, [{alice, 1}], fun(Alice) ->
  454:         Sender = {alice, Alice},
  455:         RoomID = given_new_room_with_users(Sender, []),
  456:         Result = given_message_sent_to_room(RoomID, Sender, <<"This is not JSON object">>),
  457:         ?assertMatch({{<<"400">>, <<"Bad Request">>}, <<"Request body is not a valid JSON">>}, Result)
  458:     end).
  459: 
  460: messages_are_archived_in_room(Config) ->
  461:     escalus:fresh_story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) ->
  462:         {RoomID, _Msgs} = given_new_room_with_users_and_msgs({alice, Alice}, [{bob, Bob}]),
  463:         mam_helper:maybe_wait_for_archive(Config),
  464:         {{<<"200">>, <<"OK">>}, Result} = get_room_messages({alice, Alice}, RoomID),
  465:         [Aff, _Msg1, _Msg2] = rest_helper:decode_maplist(Result),
  466:         %% The oldest message is aff change
  467:         <<"affiliation">> = maps:get(type, Aff),
  468:         <<"member">> = maps:get(affiliation, Aff),
  469:         BobJID = escalus_utils:jid_to_lower(escalus_client:short_jid(Bob)),
  470:         BobJID = maps:get(user, Aff)
  471:     end).
  472: 
  473: chat_markers_are_archived_in_room(Config) ->
  474:     escalus:fresh_story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) ->
  475:         % GIVEN 3 different chat markers that are sent via HTTP and received via XMPP
  476:         MarkedID = <<"RagnarokIsComing">>,
  477:         MarkerTypes = [<<"received">>, <<"displayed">>, <<"acknowledged">>],
  478:         Markers = [#{ chat_marker => #{ type => Type, id => MarkedID } } || Type <- MarkerTypes ],
  479:         RoomID = given_new_room_with_users({alice, Alice}, [{bob, Bob}]),
  480:         lists:foreach(fun(Marker) ->
  481:                               {{<<"200">>, <<"OK">>}, {_Result}} =
  482:                               given_message_sent_to_room(RoomID, {bob, Bob}, Marker),
  483:                               [ escalus:wait_for_stanza(Client) || Client <- [Alice, Bob] ]
  484:                       end, Markers),
  485:         mam_helper:maybe_wait_for_archive(Config),
  486: 
  487:         % WHEN an archive is queried via HTTP
  488:         {{<<"200">>, <<"OK">>}, Result} = get_room_messages({alice, Alice}, RoomID),
  489: 
  490:         % THEN these markers are retrieved and in proper order and with valid payload
  491:         % (we discard remaining msg fields, they are tested by other cases)
  492:         [_Aff | ReceivedMarkers] = rest_helper:decode_maplist(Result),
  493:         Markers = [ maps:with([chat_marker], RecvMarker) || RecvMarker <- ReceivedMarkers ]
  494:     end).
  495: 
  496: % Combo test case which verifies both the translation of "markable" element
  497: % (JSON -> XML -> JSON) and if it's preserved properly in the archive
  498: markable_property_is_archived_in_room(Config) ->
  499:     escalus:fresh_story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) ->
  500:         % GIVEN a markable message is sent in the room
  501:         MarkableMsg = #{ markable => true, body => <<"Floor is lava!">> },
  502:         RoomID = given_new_room_with_users({alice, Alice}, [{bob, Bob}]),
  503:         {{<<"200">>, <<"OK">>}, {_Result}}
  504:         = given_message_sent_to_room(RoomID, {bob, Bob}, MarkableMsg),
  505:         [ escalus:wait_for_stanza(Client) || Client <- [Alice, Bob] ],
  506:         mam_helper:maybe_wait_for_archive(Config),
  507: 
  508:         % WHEN an archive is queried via HTTP
  509:         {{<<"200">>, <<"OK">>}, Result} = get_room_messages({alice, Alice}, RoomID),
  510: 
  511:         % THEN the retrieved message has markable property
  512:         [_Aff, Msg] = rest_helper:decode_maplist(Result),
  513:         true = maps:get(markable, Msg, undefined)
  514:     end).
  515: 
  516: only_room_participant_can_read_messages(Config) ->
  517:     escalus:fresh_story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) ->
  518:         RoomID = given_new_room({alice, Alice}),
  519:         {{<<"403">>, <<"Forbidden">>}, _} = get_room_messages({bob, Bob}, RoomID),
  520:         ok
  521:     end).
  522: 
  523: get_room_messages(Caller, RoomID) ->
  524:     Path = <<"/rooms/", RoomID/binary, "/messages">>,
  525:     Creds = credentials(Caller),
  526:     rest_helper:gett(client, Path, Creds).
  527: 
  528: messages_can_be_paginated_in_room(Config) ->
  529:     escalus:fresh_story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) ->
  530:         RoomID = given_new_room_with_users({alice, Alice}, [{bob, Bob}]),
  531:         %% GenMsgs1 is older than GenMsgs2
  532:         %% One message is already in the archive
  533:         [GenMsgs1, GenMsgs2 | _] = rest_helper:fill_room_archive(RoomID, [Alice, Bob], 1),
  534:         mam_helper:maybe_wait_for_archive(Config),
  535:         Msgs10 = get_room_messages({alice, Alice}, RoomID, 10),
  536:         Msgs10Len = length(Msgs10),
  537:         true = Msgs10Len > 0 andalso Msgs10Len =< 10,
  538:         Msgs3 = get_room_messages({alice, Alice}, RoomID, 3),
  539:         [_, _, _] = Msgs3,
  540:         {_, Time} = calendar:now_to_datetime(os:timestamp()),
  541:         PriorTo = rest_helper:make_timestamp(-1, Time) - timer:seconds(10),
  542:         [OldestMsg1 | _] = get_room_messages({alice, Alice}, RoomID, 4, PriorTo),
  543:         assert_room_messages(OldestMsg1, hd(lists:keysort(1, GenMsgs1))),
  544:         [OldestMsg2 | _] = get_room_messages({alice, Alice}, RoomID, 2, PriorTo),
  545:         assert_room_messages(OldestMsg2, hd(lists:keysort(1, GenMsgs2)))
  546:     end).
  547: 
  548: room_is_created_with_given_jid(Config) ->
  549:     escalus:fresh_story(Config, [{alice, 1}], fun(Alice) ->
  550:         RoomID = muc_helper:fresh_room_name(),
  551:         RoomJID = room_jid(RoomID, Config),
  552:         RoomID = given_new_room({alice, Alice}, RoomJID),
  553:         RoomInfo = get_room_info({alice, Alice}, RoomID),
  554:         assert_room_info(Alice, RoomInfo)
  555:     end).
  556: 
  557: room_is_not_created_with_jid_not_matching_hostname(Config) ->
  558:     escalus:fresh_story(Config, [{alice, 1}], fun(Alice) ->
  559:         RoomID = muc_helper:fresh_room_name(),
  560:         RoomJID = <<RoomID/binary, "@muclight.wrongdomain">>,
  561:         Creds = credentials({alice, Alice}),
  562:         {{Status, _}, _} = create_room_with_id_request(Creds,
  563:                                                        <<"some_name">>,
  564:                                                        <<"some subject">>,
  565:                                                        RoomJID),
  566:         ?assertEqual(<<"400">>, Status)
  567:     end).
  568: 
  569: room_can_be_fetched_by_jid(Config) ->
  570:     escalus:fresh_story(Config, [{alice, 1}], fun(Alice) ->
  571:         RoomID = muc_helper:fresh_room_name(),
  572:         RoomJID = room_jid(RoomID, Config),
  573:         RoomID = given_new_room({alice, Alice}, RoomJID),
  574:         RoomInfo = get_room_info({alice, Alice}, RoomJID),
  575:         assert_room_info(Alice, RoomInfo)
  576:     end).
  577: 
  578: messages_can_be_sent_and_fetched_by_room_jid(Config) ->
  579:     escalus:fresh_story(Config, [{alice, 1}, {bob, 1}], fun(Alice, _Bob) ->
  580:         RoomID = given_new_room({alice, Alice}),
  581:         RoomJID = room_jid(RoomID, Config),
  582:         given_message_sent_to_room(RoomJID, {alice, Alice}),
  583:         mam_helper:maybe_wait_for_archive(Config),
  584:         [_] = get_room_messages({alice, Alice}, RoomJID, 10)
  585:     end).
  586: 
  587: user_can_be_added_and_removed_by_room_jid(Config) ->
  588:     escalus:fresh_story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) ->
  589:         RoomID = given_new_room({alice, Alice}),
  590:         RoomJID = room_jid(RoomID, Config),
  591:         given_user_invited({alice, Alice}, RoomJID, Bob),
  592:         {{Status, _}, _} = remove_user_from_a_room({alice, Alice}, RoomJID, Bob),
  593:         ?assertEqual(<<"204">>, Status)
  594:     end).
  595: 
  596: msg_with_props_is_sent_and_delivered_over_xmpp(Config) ->
  597:     escalus:fresh_story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) ->
  598:         BobJID = user_jid(Bob),
  599:         MsgID = base16:encode(crypto:strong_rand_bytes(5)),
  600:         M1 = rest_helper:make_msg_stanza_with_props(BobJID,MsgID),
  601: 
  602:         escalus:send(Alice, M1),
  603: 
  604:         M2 = escalus:wait_for_stanza(Bob),
  605:         escalus:assert(is_message, M2)
  606:     end).
  607: 
  608: msg_with_props_can_be_parsed(Config) ->
  609:     escalus:fresh_story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) ->
  610:         AliceJID = escalus_utils:jid_to_lower(escalus_client:short_jid(Alice)),
  611:         BobJID = escalus_utils:jid_to_lower(escalus_client:short_jid(Bob)),
  612:         MsgID = base16:encode(crypto:strong_rand_bytes(5)),
  613:         M1 = rest_helper:make_msg_stanza_with_props(BobJID,MsgID),
  614: 
  615:         escalus:send(Alice, M1),
  616: 
  617:         escalus:wait_for_stanza(Bob),
  618:         mam_helper:wait_for_archive_size(Bob, 1),
  619:         mam_helper:wait_for_archive_size(Alice, 1),
  620: 
  621:         AliceCreds = {AliceJID, user_password(alice)},
  622: 
  623:         % recent msgs with a limit
  624:         M2 = get_messages_with_props(AliceCreds, BobJID, 1),
  625: 
  626:         [{MsgWithProps} | _] = M2,
  627: 
  628:         Data = maps:from_list(MsgWithProps),
  629: 
  630:         #{<<"properties">> := {Props},
  631:           <<"id">> := ReceivedMsgID} = Data,
  632: 
  633:         %we are expecting two properties:"some_string" and "some_number" for this test message
  634:         %test message defined in rest_helper:make_msg_stanza_with_props
  635:         2 = length(Props),
  636:         ReceivedMsgID = MsgID
  637: 
  638:     end).
  639: 
  640: msg_with_malformed_props_is_sent_and_delivered_over_xmpp(Config) ->
  641:     escalus:fresh_story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) ->
  642:         BobJID = user_jid(Bob),
  643:         MsgID = base16:encode(crypto:strong_rand_bytes(5)),
  644: 
  645:         M1 = rest_helper:make_malformed_msg_stanza_with_props(BobJID, MsgID),
  646: 
  647:         escalus:send(Alice, M1),
  648: 
  649:         M2 = escalus:wait_for_stanza(Bob),
  650:         escalus:assert(is_message, M2)
  651:     end).
  652: 
  653: msg_with_malformed_props_can_be_parsed(Config) ->
  654:     escalus:fresh_story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) ->
  655:         AliceJID = escalus_utils:jid_to_lower(escalus_client:short_jid(Alice)),
  656:         AliceCreds = {AliceJID, user_password(alice)},
  657:         BobJID = escalus_utils:jid_to_lower(escalus_client:short_jid(Bob)),
  658:         MsgID = base16:encode(crypto:strong_rand_bytes(5)),
  659: 
  660:         M1 = rest_helper:make_malformed_msg_stanza_with_props(BobJID,MsgID),
  661:         escalus:send(Alice, M1),
  662: 
  663:         escalus:wait_for_stanza(Bob),
  664:         mam_helper:wait_for_archive_size(Bob, 1),
  665:         mam_helper:wait_for_archive_size(Alice, 1),
  666: 
  667:         % recent msgs with a limit
  668:         M2 = get_messages_with_props(AliceCreds, BobJID, 1),
  669:         [_Msg] = rest_helper:decode_maplist(M2),
  670: 
  671:         MsgID = maps:get(id, _Msg)
  672: 
  673:     end).
  674: 
  675: msg_with_thread_is_sent_and_delivered_over_xmpp(Config) ->
  676:     escalus:fresh_story(Config, [{alice, 1}, {bob, 1}], fun (Alice, Bob) ->
  677: 				BobJID = user_jid(Bob),
  678: 				MsgID = base16:encode(crypto:strong_rand_bytes(5)),
  679: 				ThreadID = base16:encode(crypto:strong_rand_bytes(5)),
  680: 				M1 = rest_helper:make_msg_stanza_with_thread(BobJID, MsgID, ThreadID),
  681: 				escalus:send(Alice, M1),
  682: 				M2 = escalus:wait_for_stanza(Bob),
  683: 				escalus:assert(is_message, M2)
  684: 		end).
  685: 
  686: msg_with_thread_can_be_parsed(Config) ->
  687:     escalus:fresh_story(Config, [{alice, 1}, {bob, 1}], fun (Alice, Bob) ->
  688: 				AliceJID = escalus_utils:jid_to_lower(escalus_client:short_jid(Alice)),
  689: 				BobJID = escalus_utils:jid_to_lower(escalus_client:short_jid(Bob)),
  690: 				MsgID = base16:encode(crypto:strong_rand_bytes(5)),
  691: 				ThreadID = base16:encode(crypto:strong_rand_bytes(5)),
  692: 				M1 = rest_helper:make_msg_stanza_with_thread(BobJID, MsgID, ThreadID),
  693: 				escalus:send(Alice, M1),
  694: 				escalus:wait_for_stanza(Bob),
  695: 				mam_helper:wait_for_archive_size(Bob, 1),
  696: 				mam_helper:wait_for_archive_size(Alice, 1),
  697: 				AliceCreds = {AliceJID, user_password(alice)},
  698: 				% recent msgs with a limit
  699: 				M2 = get_messages_with_props(AliceCreds, BobJID, 1),
  700: 				[{MsgWithProps} | _] = M2,
  701: 				Data = maps:from_list(MsgWithProps),
  702: 				#{<<"thread">> := ReceivedThreadID,
  703: 				  <<"id">> := ReceivedMsgID} = Data,
  704: 				%we are expecting thread and parent thread for this test message
  705: 				%test message defined in rest_helper:make_msg_stanza_with_thread
  706: 				ReceivedThreadID = ThreadID,
  707: 				ReceivedMsgID = MsgID
  708: 		end).
  709: 
  710: msg_with_thread_and_parent_is_sent_and_delivered_over_xmpp(Config) ->
  711:     escalus:fresh_story(Config, [{alice, 1}, {bob, 1}], fun (Alice, Bob) ->
  712: 				BobJID = user_jid(Bob),
  713: 				MsgID = base16:encode(crypto:strong_rand_bytes(5)),
  714: 				ThreadID = base16:encode(crypto:strong_rand_bytes(5)),
  715: 				ThreadParentID = base16:encode(crypto:strong_rand_bytes(5)),
  716: 				M1 = rest_helper:make_msg_stanza_with_thread_and_parent(BobJID, MsgID, ThreadID, ThreadParentID),
  717: 				escalus:send(Alice, M1),
  718: 				M2 = escalus:wait_for_stanza(Bob),
  719: 				escalus:assert(is_message, M2)
  720: 		end).
  721: 
  722: msg_with_thread_and_parent_can_be_parsed(Config) ->
  723:     escalus:fresh_story(Config, [{alice, 1}, {bob, 1}], fun (Alice, Bob) ->
  724: 				AliceJID = escalus_utils:jid_to_lower(escalus_client:short_jid(Alice)),
  725: 				BobJID = escalus_utils:jid_to_lower(escalus_client:short_jid(Bob)),
  726: 				MsgID = base16:encode(crypto:strong_rand_bytes(5)),
  727: 				ThreadID = base16:encode(crypto:strong_rand_bytes(5)),
  728: 				ThreadParentID = base16:encode(crypto:strong_rand_bytes(5)),
  729: 				M1 = rest_helper:make_msg_stanza_with_thread_and_parent(BobJID, MsgID, ThreadID, ThreadParentID),
  730: 				escalus:send(Alice, M1),
  731: 				escalus:wait_for_stanza(Bob),
  732: 				mam_helper:wait_for_archive_size(Bob, 1),
  733: 				mam_helper:wait_for_archive_size(Alice, 1),
  734: 				AliceCreds = {AliceJID, user_password(alice)},
  735: 				% recent msgs with a limit
  736: 				M2 = get_messages_with_props(AliceCreds, BobJID, 1),
  737: 				[{MsgWithProps} | _] = M2,
  738: 				Data = maps:from_list(MsgWithProps),
  739: 				#{<<"thread">> := ReceivedThreadID,
  740: 				  <<"parent">> := ReceivedThreadParentID,
  741: 				  <<"id">> := ReceivedMsgID} = Data,
  742: 				%we are expecting thread and parent thread for this test message
  743: 				%test message defined in rest_helper:make_msg_stanza_with_thread
  744: 				ReceivedThreadID = ThreadID,
  745: 				ReceivedThreadParentID = ThreadParentID,
  746: 				ReceivedMsgID = MsgID
  747: 		end).
  748: 
  749: msg_without_thread_is_sent_and_delivered_over_xmpp(Config) ->
  750:     escalus:fresh_story(Config, [{alice, 1}, {bob, 1}], fun (Alice, Bob) ->
  751: 				BobJID = user_jid(Bob),
  752: 				MsgID = base16:encode(crypto:strong_rand_bytes(5)),
  753: 				M1 = rest_helper:make_msg_stanza_without_thread(BobJID, MsgID),
  754: 				escalus:send(Alice, M1),
  755: 				M2 = escalus:wait_for_stanza(Bob),
  756: 				escalus:assert(is_message, M2)
  757: 		end).
  758: 
  759: msg_without_thread_can_be_parsed(Config) ->
  760:     escalus:fresh_story(Config, [{alice, 1}, {bob, 1}], fun (Alice, Bob) ->
  761: 				AliceJID = escalus_utils:jid_to_lower(escalus_client:short_jid(Alice)),
  762: 				AliceCreds = {AliceJID, user_password(alice)},
  763: 				BobJID = escalus_utils:jid_to_lower(escalus_client:short_jid(Bob)),
  764: 				MsgID = base16:encode(crypto:strong_rand_bytes(5)),
  765: 				M1 = rest_helper:make_msg_stanza_without_thread(BobJID, MsgID),
  766: 				escalus:send(Alice, M1),
  767: 				escalus:wait_for_stanza(Bob),
  768: 				mam_helper:wait_for_archive_size(Bob, 1),
  769: 				mam_helper:wait_for_archive_size(Alice, 1),
  770: 				% recent msgs with a limit
  771: 				M2 = get_messages_with_props(AliceCreds, BobJID, 1),
  772: 				[_Msg] = rest_helper:decode_maplist(M2),
  773: 				MsgID = maps:get(id, _Msg)
  774: 		end).
  775: 
  776: assert_room_messages(RecvMsg, {_ID, _GenFrom, GenMsg}) ->
  777:     escalus:assert(is_chat_message, [maps:get(body, RecvMsg)], GenMsg),
  778:     ok.
  779: 
  780: get_room_info(User, RoomID) ->
  781:     Creds = credentials(User),
  782:     {{<<"200">>, <<"OK">>}, {Result}} = rest_helper:gett(client, <<"/rooms/", RoomID/binary>>,
  783:                                                          Creds),
  784:     Result.
  785: 
  786: given_new_room_with_users_and_msgs(Owner, Users) ->
  787:     RoomID = given_new_room_with_users(Owner, Users),
  788:     Msgs = [given_message_sent_to_room(RoomID, Sender) || Sender <- [Owner | Users]],
  789:     wait_for_room_msgs(Msgs, [Owner | Users]),
  790:     {RoomID, Msgs}.
  791: 
  792: wait_for_room_msgs([], _) ->
  793:     ok;
  794: wait_for_room_msgs([Msg | Rest], Users) ->
  795:     [wait_for_room_msg(Msg, User) || {_, User} <- Users],
  796:     wait_for_room_msgs(Rest, Users).
  797: 
  798: wait_for_room_msg(Msg, User) ->
  799:     Stanza = escalus:wait_for_stanza(User),
  800:     escalus:assert(is_groupchat_message, [maps:get(body, Msg)], Stanza).
  801: 
  802: given_message_sent_to_room(RoomID, Sender) ->
  803:     Body = #{body => <<"Hi all!">>},
  804:     HTTPResult = given_message_sent_to_room(RoomID, Sender, Body),
  805:     {{<<"200">>, <<"OK">>}, {Result}} = HTTPResult,
  806:     MsgId = proplists:get_value(<<"id">>, Result),
  807:     true = is_binary(MsgId),
  808:     {UserJID, _} = credentials(Sender),
  809: 
  810:     Body#{id => MsgId, from => UserJID}.
  811: 
  812: given_message_sent_to_room(RoomID, Sender, Body) ->
  813:     Creds = credentials(Sender),
  814:     Path = <<"/rooms/", RoomID/binary, "/messages">>,
  815:     rest_helper:post(client, Path, Body, Creds).
  816: 
  817: given_new_room_with_users(Owner, Users) ->
  818:     RoomID = given_new_room(Owner),
  819:     [given_user_invited(Owner, RoomID, User) || {_, User} <- Users],
  820:     RoomID.
  821: 
  822: given_new_room(Owner) ->
  823:     Creds = credentials(Owner),
  824:     RoomName = <<"new_room_name">>,
  825:     create_room(Creds, RoomName, <<"This room subject">>).
  826: 
  827: given_new_room(Owner, RoomID) ->
  828:     Creds = credentials(Owner),
  829:     RoomName = <<"new_room_name">>,
  830:     create_room_with_id(Creds, RoomName, <<"This room subject">>, RoomID).
  831: 
  832: given_new_room(Owner, RoomID, RoomName) ->
  833:     Creds = credentials(Owner),
  834:     create_room_with_id(Creds, RoomName, <<"This room subject">>, RoomID).
  835: 
  836: given_user_invited({_, Inviter} = Owner, RoomID, Invitee) ->
  837:     JID = user_jid(Invitee),
  838:     {{<<"204">>, <<"No Content">>}, _} = invite_to_room(Owner, RoomID, JID),
  839:     maybe_wait_for_aff_stanza(Invitee, Invitee),
  840:     maybe_wait_for_aff_stanza(Inviter, Invitee).
  841: 
  842: when_config_change(User, RoomID, NewName, NewSubject) ->
  843:     Creds = credentials(User),
  844:     Config = #{name => NewName, subject => NewSubject},
  845:     Path = <<"/rooms/", RoomID/binary, "/config">>,
  846:     putt(client, Path, Config, Creds).
  847: 
  848: maybe_wait_for_aff_stanza(#client{} = Client, Invitee) ->
  849:     Stanza = escalus:wait_for_stanza(Client),
  850:     assert_aff_change_stanza(Stanza, Invitee, <<"member">>);
  851: maybe_wait_for_aff_stanza(_, _) ->
  852:     ok.
  853: 
  854: invite_to_room(Inviter, RoomID, Invitee) ->
  855:     Body = #{user => Invitee},
  856:     Creds = credentials(Inviter),
  857:     rest_helper:post(client, <<"/rooms/", RoomID/binary, "/users">>, Body, Creds).
  858: 
  859: remove_user_from_a_room(Inviter, RoomID, Invitee) ->
  860:     JID = escalus_utils:jid_to_lower(escalus_client:short_jid(Invitee)),
  861:     Creds = credentials(Inviter),
  862:     Path = <<"/rooms/", RoomID/binary, "/users/", JID/binary>>,
  863:     rest_helper:delete(client, Path, Creds).
  864: 
  865: credentials({User, ClientOrSpec}) ->
  866:     {user_jid(ClientOrSpec), user_password(User)}.
  867: 
  868: user_jid(#client{} = UserClient) ->
  869:     escalus_utils:jid_to_lower(escalus_client:short_jid(UserClient));
  870: user_jid(Spec) ->
  871:     U = proplists:get_value(username, Spec),
  872:     S = proplists:get_value(server, Spec),
  873:     escalus_utils:jid_to_lower(<<U/binary, $@, S/binary>>).
  874: 
  875: user_password(User) ->
  876:     [{User, Props}] = escalus:get_users([User]),
  877:     proplists:get_value(password, Props).
  878: 
  879: send_message(User, From, To) ->
  880:     AliceJID = user_jid(From),
  881:     BobJID = user_jid(To),
  882:     M = #{to => BobJID, body => <<"hello, ", BobJID/binary, " it's me">>},
  883:     Cred = credentials({User, From}),
  884:     {{<<"200">>, <<"OK">>}, {Result}} = post(client, <<"/messages">>, M, Cred),
  885:     ID = proplists:get_value(<<"id">>, Result),
  886:     M#{id => ID, from => AliceJID}.
  887: 
  888: get_messages(MeCreds, Other, Count) ->
  889:     GetPath = lists:flatten(["/messages/",
  890:                              binary_to_list(Other),
  891:                              "?limit=", integer_to_list(Count)]),
  892:     get_messages(GetPath, MeCreds).
  893: 
  894: get_messages(Path, Creds) ->
  895:     {{<<"200">>, <<"OK">>}, Msgs} = rest_helper:gett(client, Path, Creds),
  896:     rest_helper:decode_maplist(Msgs).
  897: 
  898: get_messages(MeCreds, Other, Before, Count) ->
  899:     GetPath = lists:flatten(["/messages/",
  900:                              binary_to_list(Other),
  901:                              "?before=", integer_to_list(Before),
  902:                              "&limit=", integer_to_list(Count)]),
  903:     get_messages(GetPath, MeCreds).
  904: 
  905: get_messages_with_props(MeCreds, Other, Count) ->
  906:     GetPath = lists:flatten(["/messages/",
  907:                              binary_to_list(Other),
  908:                              "?limit=", integer_to_list(Count)]),
  909:     get_messages_with_props(GetPath, MeCreds).
  910: 
  911: get_messages_with_props(Path, Creds) ->
  912:     {{<<"200">>, <<"OK">>}, Msgs} = rest_helper:gett(client, Path, Creds),
  913:     Msgs.
  914: 
  915: get_messages_with_props(MeCreds, Other, Before, Count) ->
  916:     GetPath = lists:flatten(["/messages/",
  917:                              binary_to_list(Other),
  918:                              "?before=", integer_to_list(Before),
  919:                              "&limit=", integer_to_list(Count)]),
  920:     get_messages_with_props(GetPath, MeCreds).
  921: 
  922: get_room_messages(Client, RoomID, Count) ->
  923:     get_room_messages(Client, RoomID, Count, undefined).
  924: 
  925: get_room_messages(Client, RoomID, Count, Before) ->
  926:     Creds = credentials(Client),
  927:     BasePathList = ["/rooms/", RoomID, "/messages?limit=", integer_to_binary(Count)],
  928:     PathList = BasePathList ++ [["&before=", integer_to_binary(Before)] || Before /= undefined],
  929:     Path = erlang:iolist_to_binary(PathList),
  930:     get_messages(Path, Creds).
  931: 
  932: create_room({_AliceJID, _} = Creds, RoomName, Subject) ->
  933:     Room = #{name => RoomName,
  934:              subject => Subject},
  935:     {{<<"200">>, <<"OK">>}, {Result}} = rest_helper:post(client, <<"/rooms">>, Room, Creds),
  936:     proplists:get_value(<<"id">>, Result).
  937: 
  938: create_room_with_id({_AliceJID, _} = Creds, RoomName, Subject, RoomID) ->
  939:     Res = create_room_with_id_request(Creds, RoomName, Subject, RoomID),
  940:     case Res of
  941:         {{<<"201">>, <<"Created">>}, {Result}} ->
  942:             proplists:get_value(<<"id">>, Result);
  943:         _ ->
  944:             ct:fail(#{issue => create_room_with_id_failed,
  945:                       result => Res,
  946:                       creds => Creds,
  947:                       room_name => RoomName,
  948:                       subject => Subject,
  949:                       room_id => RoomID})
  950:     end.
  951: 
  952: create_room_with_id_request(Creds, RoomName, Subject, RoomID) ->
  953:     Room = #{name => RoomName,
  954:              subject => Subject},
  955:     Path = <<"/rooms/", RoomID/binary>>,
  956:     putt(client, Path, Room, Creds).
  957: 
  958: get_my_rooms(User) ->
  959:     Creds = credentials(User),
  960:     {{<<"200">>, <<"OK">>}, Rooms} = rest_helper:gett(client, <<"/rooms">>, Creds),
  961:     Rooms.
  962: 
  963: assert_messages([], []) ->
  964:     ok;
  965: assert_messages([SentMsg | SentRest], [RecvMsg | RecvRest]) ->
  966:     FromJID = maps:get(from, SentMsg),
  967:     FromJID = maps:get(from, RecvMsg),
  968:     MsgId = maps:get(id, SentMsg),
  969:     MsgId = maps:get(id, RecvMsg), %checks if there is an ID
  970:     _ = maps:get(timestamp, RecvMsg), %checks if there ia timestamp
  971:     MsgBody = maps:get(body, SentMsg),
  972:     MsgBody = maps:get(body, RecvMsg),
  973:     assert_messages(SentRest, RecvRest);
  974: assert_messages(_Sent, _Recv) ->
  975:     ct:fail("Send and Recv messages are not equal").
  976: 
  977: send_messages(Config, Alice, Bob, Kate) ->
  978:     M1 = send_message(bob, Bob, Alice),
  979:     M2 = send_message(alice, Alice, Bob),
  980:     M3 = send_message(kate, Kate, Alice),
  981:     mam_helper:maybe_wait_for_archive(Config),
  982:     [M1, M2, M3].
  983: 
  984: assert_aff_change_stanza(Stanza, Target, Change) ->
  985:     TargetJID = escalus_utils:jid_to_lower(escalus_client:short_jid(Target)),
  986:     ID = exml_query:attr(Stanza, <<"id">>),
  987:     true = is_binary(ID) andalso ID /= <<>>,
  988:     Users = exml_query:paths(Stanza, [{element, <<"x">>}, {element, <<"user">>}]),
  989:     [User] = [User || User <- Users, TargetJID == exml_query:cdata(User)],
  990:     Change = exml_query:attr(User, <<"affiliation">>),
  991:     TargetJID = exml_query:cdata(User).
  992: 
  993: assert_room_info(Owner, RoomInfo) ->
  994:     true = is_property_present(<<"subject">>, RoomInfo),
  995:     true = is_property_present(<<"name">>, RoomInfo),
  996:     true = is_property_present(<<"participants">>, RoomInfo),
  997:     true = is_participant(Owner, <<"owner">>, RoomInfo).
  998: 
  999: is_property_present(Name, Proplist) ->
 1000:     Val = proplists:get_value(Name, Proplist),
 1001:     Val /= undefined.
 1002: 
 1003: assert_property_value(Name, Value, Proplist) ->
 1004:     Val = proplists:get_value(Name, Proplist),
 1005:     ?assertEqual(Value, Val).
 1006: 
 1007: is_participant(User, Role, RoomInfo) ->
 1008:     Participants = proplists:get_value(<<"participants">>, RoomInfo),
 1009:     JID = user_jid(User),
 1010:     Fun = fun({Props}) ->
 1011:                   UserJID = proplists:get_value(<<"user">>, Props),
 1012:                   UserRole = proplists:get_value(<<"role">>, Props),
 1013:                   UserJID == JID andalso UserRole == Role
 1014:           end,
 1015:     lists:any(Fun, Participants).
 1016: 
 1017: connect_to_sse(User) ->
 1018:     Port = ct:get_config({hosts, mim, http_api_client_endpoint_port}),
 1019:     {Username, Password} = credentials(User),
 1020:     Base64 = base64:encode(binary_to_list(Username) ++ [$: | binary_to_list(Password)]),
 1021:     Headers = [{<<"authorization">>, <<"basic ", Base64/binary>>},
 1022:                {<<"host">>, <<"localhost">>},
 1023:                {<<"accept">>, <<"text/event-stream">>}],
 1024:     {ok, ConnPid} = gun:open("localhost", Port, #{
 1025:         transport => tls,
 1026:         protocols => [http],
 1027:         http_opts => #{content_handlers => [gun_sse_h, gun_data_h]}
 1028:     }),
 1029:     {ok, _} = gun:await_up(ConnPid),
 1030:     StreamRef = gun:get(ConnPid, "/api/sse", Headers),
 1031:     #{pid => ConnPid, stream_ref => StreamRef}.
 1032: 
 1033: wait_for_event(#{pid := Pid, stream_ref := StreamRef} = Opts) ->
 1034:     case gun:await(Pid, StreamRef) of
 1035:         {response, nofin, _Status, _} ->
 1036:             wait_for_event(Opts);
 1037:         {sse, #{data := [Response]}} ->
 1038:           Opts#{data => Response};
 1039:         Error ->
 1040:             Error
 1041:     end.
 1042: 
 1043: stop_sse(#{pid := Pid}) ->
 1044:     gun:close(Pid).
 1045: 
 1046: assert_json_message(Sent, Received) ->
 1047:     #{<<"body">> := Body,
 1048:       <<"to">> := To,
 1049:       <<"from">> := From,
 1050:       <<"id">> := Id} = Received,
 1051: 
 1052:     Body = maps:get(body, Sent),
 1053:     To = maps:get(to, Sent),
 1054:     From = maps:get(from, Sent),
 1055:     Id = maps:get(id, Sent).
 1056: 
 1057: assert_json_room_sse_message(Expected, Received) ->
 1058:     #{<<"from">> := From,
 1059:       <<"room">> := Room,
 1060:       <<"id">> := _Id,
 1061:       <<"type">> := Type} = Received,
 1062: 
 1063:     Room = maps:get(room, Expected),
 1064:     Type = maps:get(type, Expected),
 1065:     From = maps:get(from, Expected),
 1066:     case Type of
 1067:         <<"message">> ->
 1068:             Body = maps:get(<<"body">>, Received),
 1069:             Body = maps:get(body, Expected);
 1070:         _ ->
 1071:             User = maps:get(<<"user">>, Received),
 1072:             User = maps:get(user, Expected)
 1073:     end.
 1074: 
 1075: 
 1076: add_contact_and_invite(Config) ->
 1077:     escalus:fresh_story(
 1078:         Config, [{alice, 1}, {bob, 1}],
 1079:         fun(Alice, Bob) ->
 1080:             AliceJID = escalus_utils:jid_to_lower(
 1081:                             escalus_client:short_jid(Alice)),
 1082:             BCred = credentials({bob, Bob}),
 1083:             % bob has empty roster
 1084:             {?OK, R} = gett(client, "/contacts", BCred),
 1085:             Res = decode_maplist(R),
 1086:             [] = Res,
 1087:             % adds Alice
 1088:             add_contact_check_roster_push(Alice, {bob, Bob}),
 1089:             % and she is in his roster, with empty status
 1090:             {?OK, R2} = gett(client, "/contacts", BCred),
 1091:             Result = decode_maplist(R2),
 1092:             [Res2] = Result,
 1093:             #{jid := AliceJID, subscription := <<"none">>,
 1094:               ask := <<"none">>} = Res2,
 1095:             % he invites her
 1096:             PutPath = lists:flatten(["/contacts/", binary_to_list(AliceJID)]),
 1097:             {?NOCONTENT, _} = putt(client, PutPath,
 1098:                                    #{action => <<"invite">>},
 1099:                                    BCred),
 1100:             % another roster push
 1101:             Push2 = escalus:wait_for_stanza(Bob),
 1102:             escalus:assert(is_roster_set, Push2),
 1103:             ct:log("Push2: ~p", [Push2]),
 1104:             % she receives  a subscription request
 1105:             Sub = escalus:wait_for_stanza(Alice),
 1106:             escalus:assert(is_presence_with_type, [<<"subscribe">>], Sub),
 1107:             % in his roster she has a changed 'ask' status
 1108:             {?OK, R3} = gett(client, "/contacts", BCred),
 1109:             Result3 = decode_maplist(R3),
 1110:             [Res3] = Result3,
 1111:             #{jid := AliceJID, subscription := <<"none">>,
 1112:               ask := <<"out">>} = Res3,
 1113:             % adds him to her contacts
 1114:             escalus:send(Alice, escalus_stanza:roster_add_contact(Bob,
 1115:                          [], <<"Bob">>)),
 1116:             PushReqB = escalus:wait_for_stanza(Alice),
 1117:             escalus:assert(is_roster_set, PushReqB),
 1118:             escalus:send(Alice, escalus_stanza:iq_result(PushReqB)),
 1119:             escalus:assert(is_iq_result, escalus:wait_for_stanza(Alice)),
 1120:             %% Alice sends subscribed presence
 1121:             escalus:send(Alice,
 1122:                          escalus_stanza:presence_direct(
 1123:                              escalus_client:short_jid(Bob),
 1124:                              <<"subscribed">>)),
 1125:             %% Wait for push before trying to query endpoint
 1126:             %% If we just call endpoint,
 1127:             %% the "subscribed" stanza can not yet be processed.
 1128:             Push3 = escalus:wait_for_stanza(Bob),
 1129:             ct:log("Push3 ~p", [Push3]),
 1130:             escalus:assert(is_roster_set, Push3),
 1131: 
 1132:             % now check Bob's roster
 1133:             {?OK, R4} = gett(client, "/contacts", BCred),
 1134:             Result4 = decode_maplist(R4),
 1135:             [Res4] = Result4,
 1136:             #{jid := AliceJID, subscription := <<"to">>,
 1137:                 ask := <<"none">>} = Res4,
 1138:             ok
 1139:         end
 1140:     ),
 1141:     ok.
 1142: 
 1143: add_contact_and_be_invited(Config) ->
 1144:     escalus:fresh_story(
 1145:         Config, [{alice, 1}, {bob, 1}],
 1146:         fun(Alice, Bob) ->
 1147:             AliceJID = escalus_utils:jid_to_lower(
 1148:                 escalus_client:short_jid(Alice)),
 1149:             BCred = credentials({bob, Bob}),
 1150:             % bob has empty roster
 1151:             {?OK, R} = gett(client, "/contacts", BCred),
 1152:             Res = decode_maplist(R),
 1153:             [] = Res,
 1154:             % adds Alice
 1155:             add_contact_check_roster_push(Alice, {bob, Bob}),
 1156:             % and she is in his roster, with empty status
 1157:             {?OK, R2} = gett(client, "/contacts", BCred),
 1158:             Result = decode_maplist(R2),
 1159:             [Res2] = Result,
 1160:             #{jid := AliceJID, subscription := <<"none">>,
 1161:               ask := <<"none">>} = Res2,
 1162:             %% she adds him and invites
 1163:             escalus:send(Alice, escalus_stanza:roster_add_contact(Bob,
 1164:                          [],
 1165:                          <<"Bobek">>)),
 1166:             escalus:assert_many([is_roster_set, is_iq_result],
 1167:                                 escalus:wait_for_stanzas(Alice, 2)),
 1168:             escalus:send(Alice,
 1169:                          escalus_stanza:presence_direct(
 1170:                              escalus_client:short_jid(Bob),
 1171:                              <<"subscribe">>)),
 1172:             escalus:assert(is_roster_set, escalus:wait_for_stanza(Alice)),
 1173:             escalus:assert(is_presence_with_type, [<<"subscribe">>],
 1174:                            escalus:wait_for_stanza(Bob)),
 1175:             % now check Bob's roster, and it is the same...
 1176:             {?OK, R4} = gett(client, "/contacts", BCred),
 1177:             [Res4] = decode_maplist(R4),
 1178:             #{jid := AliceJID, subscription := <<"none">>,
 1179:                 ask := <<"in">>} = Res4,
 1180:             % because although it is stated in RFC3921, 8.2.6 that {none, in}
 1181:             % should be hidden from user, we changed it in REST API
 1182:             % he accepts
 1183:             PutPath = lists:flatten(["/contacts/", binary_to_list(AliceJID)]),
 1184:             {?NOCONTENT, _} = putt(client, PutPath,
 1185:                                    #{action => <<"accept">>},
 1186:                                    BCred),
 1187:             escalus:assert(is_roster_set, escalus:wait_for_stanza(Bob)),
 1188:             IsSub = fun(S) ->
 1189:                         escalus_pred:is_presence_with_type(<<"subscribed">>, S)
 1190:                     end,
 1191:             escalus:assert_many([is_roster_set, IsSub,
 1192:                                  is_presence],
 1193:                                 escalus:wait_for_stanzas(Alice, 3)),
 1194:             ok
 1195:         end
 1196:     ),
 1197:     ok.
 1198: 
 1199: is_subscription_remove(User) ->
 1200:     IsSubscriptionRemove = fun(El) ->
 1201:                 Sub = exml_query:paths(El, [{element, <<"query">>},
 1202:                                             {element, <<"item">>},
 1203:                                             {attr, <<"subscription">>}]),
 1204:                 Sub == [<<"remove">>]
 1205:                 end,
 1206:     escalus:assert(IsSubscriptionRemove, escalus:wait_for_stanza(User)).
 1207: 
 1208: 
 1209: 
 1210: add_and_remove(Config) ->
 1211:     escalus:fresh_story(
 1212:         Config, [{alice, 1}, {bob, 1}],
 1213:         fun(Alice, Bob) ->
 1214:             AliceJID = escalus_utils:jid_to_lower(
 1215:                 escalus_client:short_jid(Alice)),
 1216:             BCred = credentials({bob, Bob}),
 1217:             % adds Alice
 1218:             add_contact_check_roster_push(Alice, {bob, Bob}),
 1219:             % Check if Contact is in Bob's roster
 1220:             {?OK, R2} = gett(client, "/contacts", BCred),
 1221:             Result = decode_maplist(R2),
 1222:             [Res2] = Result,
 1223:             #{jid := AliceJID, subscription := <<"none">>,
 1224:               ask := <<"none">>} = Res2,
 1225:             % delete user
 1226:             DelPath = lists:flatten(["/contacts/", binary_to_list(AliceJID)]),
 1227:             {?NOCONTENT, _} = delete(client, DelPath, BCred),
 1228:             % Bob's roster is empty again
 1229:             {?OK, R3} = gett(client, "/contacts", BCred),
 1230:             [] = decode_maplist(R3),
 1231:             is_subscription_remove(Bob),
 1232:             ok
 1233:         end
 1234:     ),
 1235:     ok.
 1236: 
 1237: 
 1238: add_and_remove_some_contacts_properly(Config) ->
 1239:     escalus:fresh_story(
 1240:         Config, [{alice, 1}, {bob, 1}, {kate, 1}, {mike, 1}],
 1241:         fun(Alice, Bob, Kate, Mike) ->
 1242:             BCred = credentials({bob, Bob}),
 1243:             % adds all the other users
 1244:             lists:foreach(fun(AddContact) ->
 1245:                                   add_contact_check_roster_push(AddContact, {bob, Bob}) end,
 1246:                          [Alice, Kate, Mike]),
 1247:             AliceJID = escalus_utils:jid_to_lower(
 1248:                 escalus_client:short_jid(Alice)),
 1249:             KateJID = escalus_utils:jid_to_lower(
 1250:                 escalus_client:short_jid(Kate)),
 1251:             MikeJID = escalus_utils:jid_to_lower(
 1252:                 escalus_client:short_jid(Mike)),
 1253:             _AliceContact = create_contact(AliceJID),
 1254:             _KateContact = create_contact(KateJID),
 1255:             MikeContact = create_contact(MikeJID),
 1256:             % delete Alice and Kate
 1257:             Body = jiffy:encode(#{<<"to_delete">> => [AliceJID, KateJID]}),
 1258:             {?OK, {[{<<"not_deleted">>,[]}]}} = delete(client, "/contacts", BCred, Body),
 1259:             % Bob's roster consists now of only Mike
 1260:             {?OK, R4} = gett(client, "/contacts", BCred),
 1261:             [MikeContact] = decode_maplist(R4),
 1262:             is_subscription_remove(Bob),
 1263:             ok
 1264:         end
 1265:     ),
 1266:     ok.
 1267: 
 1268: 
 1269: add_and_remove_some_contacts_with_nonexisting(Config) ->
 1270:     escalus:fresh_story(
 1271:         Config, [{alice, 1}, {bob, 1}, {kate, 1}, {mike, 1}],
 1272:         fun(Alice, Bob, Kate, Mike) ->
 1273:             BCred = credentials({bob, Bob}),
 1274:             % adds all the other users
 1275:             lists:foreach(fun(AddContact) ->
 1276:                                   add_contact_check_roster_push(AddContact, {bob, Bob}) end,
 1277:                          [Alice, Kate]),
 1278:             AliceJID = escalus_utils:jid_to_lower(
 1279:                 escalus_client:short_jid(Alice)),
 1280:             KateJID = escalus_utils:jid_to_lower(
 1281:                 escalus_client:short_jid(Kate)),
 1282:             MikeJID = escalus_utils:jid_to_lower(
 1283:                 escalus_client:short_jid(Mike)),
 1284:             _AliceContact = create_contact(AliceJID),
 1285:             _KateContact = create_contact(KateJID),
 1286:             _MikeContact = create_contact(MikeJID),
 1287:             % delete Alice, Kate and Mike (who is absent)
 1288:             Body = jiffy:encode(#{<<"to_delete">> => [AliceJID, KateJID, MikeJID]}),
 1289:             {?OK, {[{<<"not_deleted">>,[MikeJID]}]}} = delete(client, "/contacts", BCred, Body),
 1290:             % Bob's roster is empty now
 1291:             {?OK, R4} = gett(client, "/contacts", BCred),
 1292:             [] = decode_maplist(R4),
 1293:             is_subscription_remove(Bob),
 1294:             ok
 1295:         end
 1296:     ),
 1297:     ok.
 1298: 
 1299: create_contact(JID) ->
 1300:     #{jid => JID, subscription => <<"none">>,
 1301:                              ask => <<"none">>}.
 1302: 
 1303: add_contact_check_roster_push(Contact, {_, RosterOwnerSpec} = RosterOwner) ->
 1304:     ContactJID = escalus_utils:jid_to_lower(
 1305:                 escalus_client:short_jid(Contact)),
 1306:     RosterOwnerCreds = credentials(RosterOwner),
 1307:     {?NOCONTENT, _} = post(client, <<"/contacts">>, #{jid => ContactJID},
 1308:                             RosterOwnerCreds),
 1309:     Push = escalus:wait_for_stanza(RosterOwnerSpec),
 1310:     escalus:assert(is_roster_set, Push),
 1311:     ok.
 1312: 
 1313: 
 1314: break_stuff(Config) ->
 1315:     escalus:fresh_story(
 1316:         Config, [{alice, 1}, {bob, 1}],
 1317:         fun(Alice, Bob) ->
 1318:             AliceJID = escalus_utils:jid_to_lower(
 1319:                 escalus_client:short_jid(Alice)),
 1320:             BCred = credentials({bob, Bob}),
 1321:             AddContact = #{jid => AliceJID},
 1322:             {?NOCONTENT, _} = post(client, <<"/contacts">>, AddContact,
 1323:                 BCred),
 1324:             PutPath = lists:flatten(["/contacts/", binary_to_list(AliceJID)]),
 1325:             {?NOT_IMPLEMENTED, _} = putt(client, PutPath,
 1326:                                          #{action => <<"nosuchaction">>},
 1327:                                          BCred),
 1328:             BadPutPath = "/contacts/zorro@localhost",
 1329:             {?NOT_FOUND, _} = putt(client, BadPutPath,
 1330:                                    #{action => <<"invite">>},
 1331:                                    BCred),
 1332:             BadGetPath = "/contacts/zorro@localhost",
 1333:             {?NOT_FOUND, _} = gett(client, BadGetPath, BCred),
 1334:             ok
 1335:         end
 1336:     ),
 1337:     ok.
 1338: 
 1339: -spec room_jid(RoomID :: binary(), Config :: list()) -> RoomJID :: binary().
 1340: room_jid(RoomID, Config) ->
 1341:     MUCLightHost = config_to_muc_host(Config),
 1342:     <<RoomID/binary, "@", MUCLightHost/binary>>.
 1343: 
 1344: default_http_server_name_is_returned_if_not_changed(_Config) ->
 1345:     %% GIVEN MIM1 uses default name
 1346:     verify_server_name_in_header(distributed_helper:mim(), <<"Cowboy">>).
 1347: 
 1348: non_default_http_server_name_is_returned_if_configured(_Config) ->
 1349:     %% GIVEN MIM2 uses name "Classified"
 1350:     verify_server_name_in_header(distributed_helper:mim2(), <<"Classified">>).
 1351: 
 1352: verify_server_name_in_header(Server, ExpectedName) ->
 1353:     % WHEN unathenticated user makes a request to nonexistent path
 1354:     ReqParams = #{
 1355:       role => client,
 1356:       method => <<"GET">>,
 1357:       path => "/contacts/zorro@localhost",
 1358:       body => <<>>,
 1359:       return_headers => true,
 1360:       server => Server
 1361:      },
 1362:     {?UNAUTHORIZED, Headers2, _} = rest_helper:make_request(ReqParams),
 1363:     % THEN expected server name is returned
 1364:     ExpectedName = proplists:get_value(<<"server">>, Headers2).
 1365: 
 1366: config_to_muc_host(Config) ->
 1367:     ?config(muc_light_host, Config).