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