1: -module(graphql_muc_light_SUITE).
    2: 
    3: -compile([export_all, nowarn_export_all]).
    4: 
    5: -import(distributed_helper, [mim/0, require_rpc_nodes/1, rpc/4]).
    6: -import(graphql_helper, [execute/3, execute_auth/2, get_listener_port/1,
    7:                          get_listener_config/1, get_ok_value/2, get_err_msg/1,
    8:                          make_creds/1]).
    9: 
   10: -include_lib("common_test/include/ct.hrl").
   11: -include_lib("jid/include/jid.hrl").
   12: -include_lib("eunit/include/eunit.hrl").
   13: -include_lib("exml/include/exml.hrl").
   14: 
   15: -define(UNKNOWN_DOMAIN, <<"not-existing-domain">>).
   16: -define(UNKNOWN, <<"not-existing">>).
   17: 
   18: %% GraphQL Paths
   19: -define(CREATE_ROOM_PATH, [data, muc_light, createRoom]).
   20: -define(CHANGE_CONFIG_PATH, [data, muc_light, changeRoomConfiguration]).
   21: -define(INVITE_USER_PATH, [data, muc_light, inviteUser]).
   22: -define(KICK_USER_PATH, [data, muc_light, kickUser]).
   23: -define(DELETE_ROOM_PATH, [data, muc_light, deleteRoom]).
   24: -define(SEND_MESSAGE_PATH, [data, muc_light, sendMessageToRoom]).
   25: -define(GET_MESSAGES_PATH, [data, muc_light, getRoomMessages]).
   26: -define(LIST_USER_ROOMS_PATH, [data, muc_light, listUserRooms]).
   27: -define(USER_LIST_ROOMS_PATH, [data, muc_light, listRooms]).
   28: -define(LIST_ROOM_USERS_PATH, [data, muc_light, listRoomUsers]).
   29: -define(GET_ROOM_CONFIG_PATH, [data, muc_light, getRoomConfig]).
   30: -define(GET_BLOCKING_LIST_PATH, [data, muc_light, getBlockingList]).
   31: -define(SET_BLOCKING_LIST_PATH, [data, muc_light, setBlockingList]).
   32: 
   33: suite() ->
   34:     require_rpc_nodes([mim]) ++ escalus:suite().
   35: 
   36: all() ->
   37:     [{group, user_muc_light},
   38:      {group, admin_muc_light}].
   39: 
   40: groups() ->
   41:     [{user_muc_light, [parallel], user_muc_light_handler()},
   42:      {admin_muc_light, [parallel], admin_muc_light_handler()}].
   43: 
   44: user_muc_light_handler() ->
   45:     [user_create_room,
   46:      user_create_identified_room,
   47:      user_change_room_config,
   48:      user_change_room_config_errors,
   49:      user_invite_user,
   50:      user_invite_user_errors,
   51:      user_delete_room,
   52:      user_kick_user,
   53:      user_send_message_to_room,
   54:      user_send_message_to_room_errors,
   55:      user_get_room_messages,
   56:      user_list_rooms,
   57:      user_list_room_users,
   58:      user_get_room_config,
   59:      user_blocking_list
   60:     ].
   61: 
   62: admin_muc_light_handler() ->
   63:     [admin_create_room,
   64:      admin_create_identified_room,
   65:      admin_change_room_config,
   66:      admin_change_room_config_errors,
   67:      admin_invite_user,
   68:      admin_invite_user_errors,
   69:      admin_delete_room,
   70:      admin_kick_user,
   71:      admin_send_message_to_room,
   72:      admin_send_message_to_room_errors,
   73:      admin_get_room_messages,
   74:      admin_list_user_rooms,
   75:      admin_list_room_users,
   76:      admin_get_room_config,
   77:      admin_blocking_list
   78:     ].
   79: 
   80: init_per_suite(Config) ->
   81:     Config1 = init_modules(Config),
   82:     [{muc_light_host, muc_light_helper:muc_host()}
   83:      | escalus:init_per_suite(Config1)].
   84: 
   85: end_per_suite(Config) ->
   86:     escalus_fresh:clean(),
   87:     dynamic_modules:restore_modules(Config),
   88:     escalus:end_per_suite(Config).
   89: 
   90: init_modules(Config) ->
   91:     HostType = domain_helper:host_type(),
   92:     Config1 = dynamic_modules:save_modules(HostType, Config),
   93:     Config2 = rest_helper:maybe_enable_mam(mam_helper:backend(), HostType, Config1),
   94:     dynamic_modules:ensure_modules(HostType, required_modules(suite)),
   95:     Config2.
   96: 
   97: required_modules(_) ->
   98:     [{mod_muc_light, common_muc_light_opts()}].
   99: 
  100: common_muc_light_opts() ->
  101:     MucPattern = distributed_helper:subhost_pattern(muc_light_helper:muc_host_pattern()),
  102:     [{host, MucPattern},
  103:      {rooms_in_rosters, true}].
  104: 
  105: init_per_group(admin_muc_light, Config) ->
  106:     graphql_helper:init_admin_handler(Config);
  107: init_per_group(user_muc_light, Config) ->
  108:     [{schema_endpoint, user} | Config].
  109: 
  110: end_per_group(_GN, Config) ->
  111:     Config.
  112: 
  113: init_per_testcase(TC, Config) ->
  114:     rest_helper:maybe_skip_mam_test_cases(TC, [user_get_room_messages,
  115:                                                admin_get_room_messages], Config).
  116: 
  117: end_per_testcase(TC, Config) ->
  118:     escalus:end_per_testcase(TC, Config).
  119: 
  120: %% User test cases
  121: 
  122: user_create_room(Config) ->
  123:     escalus:fresh_story_with_config(Config, [{alice, 1}], fun user_create_room_story/2).
  124: 
  125: user_create_room_story(Config, Alice) ->
  126:     Ep = ?config(schema_endpoint, Config),
  127:     MucServer = ?config(muc_light_host, Config),
  128:     AliceBinLower = escalus_utils:jid_to_lower(escalus_client:short_jid(Alice)),
  129:     Creds = make_creds(Alice),
  130:     Name = <<"first room">>,
  131:     Subject = <<"testing">>,
  132:     Res = execute(Ep, user_create_room_body(MucServer, Name, Subject, null), Creds),
  133:     #{<<"jid">> := JID, <<"name">> := Name, <<"subject">> := Subject,
  134:       <<"participants">> := Participants} = get_ok_value(?CREATE_ROOM_PATH, Res),
  135:     ?assertMatch(#jid{server = MucServer}, jid:from_binary(JID)),
  136:     ?assertEqual([#{<<"jid">> => AliceBinLower, <<"affiliation">> => <<"OWNER">>}], Participants),
  137:     % Try with a non-existent domain
  138:     Res2 = execute(Ep, user_create_room_body(?UNKNOWN_DOMAIN, Name, Subject, null), Creds),
  139:     ?assertNotEqual(nomatch, binary:match(get_err_msg(Res2), <<"not found">>)).
  140: 
  141: user_create_identified_room(Config) ->
  142:     escalus:fresh_story_with_config(Config, [{alice, 1}], fun user_create_identified_room_story/2).
  143: 
  144: user_create_identified_room_story(Config, Alice) ->
  145:     Ep = ?config(schema_endpoint, Config),
  146:     MucServer = ?config(muc_light_host, Config),
  147:     Creds = make_creds(Alice),
  148:     Name = <<"first room">>,
  149:     Subject = <<"testing">>,
  150:     Id = <<"my_user_room">>,
  151:     Res = execute(Ep, user_create_room_body(MucServer, Name, Subject, Id), Creds),
  152:     #{<<"jid">> := JID, <<"name">> := Name, <<"subject">> := Subject} =
  153:         get_ok_value(?CREATE_ROOM_PATH, Res),
  154:     ?assertMatch(#jid{user = Id, server = MucServer}, jid:from_binary(JID)),
  155:     % Create a room with an existing ID
  156:     Res2 = execute(Ep, user_create_room_body(MucServer, <<"snd room">>, Subject, Id), Creds),
  157:     ?assertNotEqual(nomatch, binary:match(get_err_msg(Res2), <<"already exists">>)),
  158:     % Try with a non-existent domain
  159:     Res3 = execute(Ep, user_create_room_body(?UNKNOWN_DOMAIN, <<"name">>, Subject, Id), Creds),
  160:     ?assertNotEqual(nomatch, binary:match(get_err_msg(Res3), <<"not found">>)),
  161:     % Try with an empty string passed as ID
  162:     Res4 = execute(Ep, user_create_room_body(MucServer, <<"name">>, Subject, <<>>), Creds),
  163:     ?assertNotEqual(nomatch, binary:match(get_coertion_err_msg(Res4), <<"Given string is empty">>)).
  164: 
  165: user_change_room_config(Config) ->
  166:     escalus:fresh_story_with_config(Config, [{alice, 1}], fun user_change_room_config_story/2).
  167: 
  168: user_change_room_config_story(Config, Alice) ->
  169:     AliceBin = escalus_client:short_jid(Alice),
  170:     Ep = ?config(schema_endpoint, Config),
  171:     MUCServer = ?config(muc_light_host, Config),
  172:     Creds = make_creds(Alice),
  173:     % Create a new room
  174:     {ok, #{jid := RoomJID}} = create_room(MUCServer, <<"ornithology">>, <<"birds">>, AliceBin),
  175:     % Try to change the room configuration
  176:     Name2 = <<"changed room">>,
  177:     Subject2 = <<"not testing">>,
  178:     Res = execute(Ep, user_change_room_configuration_body(jid:to_binary(RoomJID), Name2, Subject2), Creds),
  179:     ?assertMatch(#{<<"name">> := Name2, <<"subject">> := Subject2}, get_ok_value(?CHANGE_CONFIG_PATH, Res)).
  180: 
  181: user_change_room_config_errors(Config) ->
  182:     escalus:fresh_story_with_config(Config, [{alice, 1}, {bob, 1}],
  183:                                     fun user_change_room_config_errors_story/3).
  184: 
  185: user_change_room_config_errors_story(Config, Alice, Bob) ->
  186:     Ep = ?config(schema_endpoint, Config),
  187:     MUCServer = ?config(muc_light_host, Config),
  188:     CredsAlice = make_creds(Alice),
  189:     CredsBob = make_creds(Bob),
  190:     AliceBin = escalus_client:short_jid(Alice),
  191:     BobBin = escalus_client:short_jid(Bob),
  192:     RoomName = <<"first room">>,
  193:     {ok, #{jid := #jid{luser = RoomID} = RoomJID}} =
  194:         create_room(MUCServer, RoomName, <<"subject">>, AliceBin),
  195:     % Try to change the config with a non-existent domain
  196:     Res = execute(Ep, user_change_room_configuration_body(
  197:                         make_bare_jid(RoomID, ?UNKNOWN_DOMAIN), RoomName, <<"subject2">>), CredsAlice),
  198:     ?assertNotEqual(nomatch, binary:match(get_err_msg(Res), <<"not found">>)),
  199:     % Try to change the config of the non-existent room
  200:     Res2 = execute(Ep, user_change_room_configuration_body(
  201:                          make_bare_jid(?UNKNOWN, MUCServer), RoomName, <<"subject2">>), CredsAlice),
  202:     ?assertNotEqual(nomatch, binary:match(get_err_msg(Res2), <<"not found">>)),
  203:     % Try to change the config by the user that does not occupy this room
  204:     Res3 = execute(Ep, user_change_room_configuration_body(
  205:                          jid:to_binary(RoomJID), RoomName, <<"subject2">>), CredsBob),
  206:     ?assertNotEqual(nomatch, binary:match(get_err_msg(Res3), <<"not occupy this room">>)),
  207:     % Try to change a config by the user without permission
  208:     {ok, _} = invite_user(RoomJID, AliceBin, BobBin),
  209:     Res4 = execute(Ep, user_change_room_configuration_body(
  210:                          jid:to_binary(RoomJID), RoomName, <<"subject2">>), CredsBob),
  211:     ?assertNotEqual(nomatch, binary:match(get_err_msg(Res4),
  212:                                           <<"does not have permission to change">>)).
  213: 
  214: user_invite_user(Config) ->
  215:     escalus:fresh_story_with_config(Config, [{alice, 1}, {bob, 1}], fun user_invite_user_story/3).
  216: 
  217: user_invite_user_story(Config, Alice, Bob) ->
  218:     Ep = ?config(schema_endpoint, Config),
  219:     MUCServer = ?config(muc_light_host, Config),
  220:     CredsAlice = make_creds(Alice),
  221:     AliceBin = escalus_client:short_jid(Alice),
  222:     BobBin = escalus_client:short_jid(Bob),
  223:     Domain = escalus_client:server(Alice),
  224:     Name = <<"first room">>,
  225:     {ok, #{jid := RoomJID}} = create_room(MUCServer, Name, <<"subject2">>, AliceBin),
  226:     % Room owner can invite a user
  227:     Res = execute(Ep, user_invite_user_body(jid:to_binary(RoomJID), BobBin), CredsAlice),
  228:     ?assertNotEqual(nomatch, binary:match(get_ok_value(?INVITE_USER_PATH, Res),
  229:                                           <<"successfully">>)),
  230:     escalus:wait_for_stanza(Bob),
  231:     BobName = escalus_utils:jid_to_lower(escalus_client:username(Bob)),
  232:     AliceName = escalus_utils:jid_to_lower(escalus_client:username(Alice)),
  233:     ExpectedAff = lists:sort([{{AliceName, Domain}, owner},
  234:                               {{BobName, Domain}, member}]),
  235:     ?assertMatch(ExpectedAff, lists:sort(get_room_aff(RoomJID))).
  236: 
  237: user_invite_user_errors(Config) ->
  238:     escalus:fresh_story_with_config(Config, [{alice, 1}, {bob, 1}],
  239:                                     fun user_invite_user_errors_story/3).
  240: 
  241: user_invite_user_errors_story(Config, Alice, Bob) ->
  242:     Ep = ?config(schema_endpoint, Config),
  243:     MUCServer = ?config(muc_light_host, Config),
  244:     CredsAlice = make_creds(Alice),
  245:     CredsBob = make_creds(Bob),
  246:     AliceBin = escalus_client:short_jid(Alice),
  247:     BobBin = escalus_client:short_jid(Bob),
  248:     {ok, #{jid := #jid{luser = RoomID} = RoomJID}} =
  249:         create_room(MUCServer, <<"first room">>, <<"subject">>, AliceBin),
  250:     % Try to invite a user to not existing room
  251:     Res = execute(Ep, user_invite_user_body(
  252:                         make_bare_jid(?UNKNOWN, MUCServer), BobBin), CredsAlice),
  253:     ?assertNotEqual(nomatch, binary:match(get_err_msg(Res), <<"does not occupy this room">>)),
  254:     % User without rooms tries to invite a user
  255:     Res2 = execute(Ep, user_invite_user_body(
  256:                          jid:to_binary(RoomJID), AliceBin), CredsBob),
  257:     ?assertNotEqual(nomatch, binary:match(get_err_msg(Res2), <<"does not occupy this room">>)),
  258:     % Try with a non-existent domain
  259:     Res3 = execute(Ep, user_invite_user_body(
  260:                          make_bare_jid(RoomID, ?UNKNOWN_DOMAIN), BobBin), CredsAlice),
  261:     ?assertNotEqual(nomatch, binary:match(get_err_msg(Res3), <<"not found">>)).
  262: 
  263: user_delete_room(Config) ->
  264:     escalus:fresh_story_with_config(Config, [{alice, 1}, {bob, 1}], fun user_delete_room_story/3).
  265: 
  266: user_delete_room_story(Config, Alice, Bob) ->
  267:     Ep = ?config(schema_endpoint, Config),
  268:     MUCServer = ?config(muc_light_host, Config),
  269:     CredsAlice = make_creds(Alice),
  270:     CredsBob = make_creds(Bob),
  271:     AliceBin = escalus_client:short_jid(Alice),
  272:     BobBin = escalus_client:short_jid(Bob),
  273:     Name = <<"first room">>,
  274:     {ok, #{jid := #jid{luser = RoomID} = RoomJID}} =
  275:         create_room(MUCServer, Name, <<"subject">>, AliceBin),
  276:     {ok, _} = invite_user(RoomJID, AliceBin, BobBin),
  277:     % Member cannot delete room
  278:     Res = execute(Ep, delete_room_body(jid:to_binary(RoomJID)), CredsBob),
  279:     ?assertNotEqual(nomatch, binary:match(get_err_msg(Res),
  280:                                           <<"cannot delete this room">>)),
  281:     % Owner can delete own room
  282:     Res2 = execute(Ep, delete_room_body(jid:to_binary(RoomJID)), CredsAlice),
  283:     ?assertNotEqual(nomatch, binary:match(get_ok_value(?DELETE_ROOM_PATH, Res2),
  284:                                           <<"successfully">>)),
  285:     ?assertEqual({error, not_exists}, get_room_info(jid:from_binary(RoomJID))),
  286:     % Try with a non-existent domain
  287:     Res3 = execute(Ep, delete_room_body(make_bare_jid(RoomID, ?UNKNOWN_DOMAIN)), CredsAlice),
  288:     ?assertNotEqual(nomatch, binary:match(get_err_msg(Res3), <<"not found">>)),
  289:     % Try with a non-existent room
  290:     Res4 = execute(Ep, delete_room_body(make_bare_jid(?UNKNOWN, MUCServer)), CredsAlice),
  291:     ?assertNotEqual(nomatch, binary:match(get_err_msg(Res4), <<"not existing room">>)).
  292: 
  293: user_kick_user(Config) ->
  294:     escalus:fresh_story_with_config(Config, [{alice, 1}, {bob, 1}], fun user_kick_user_story/3).
  295: 
  296: user_kick_user_story(Config, Alice, Bob) ->
  297:     Ep = ?config(schema_endpoint, Config),
  298:     MUCServer = ?config(muc_light_host, Config),
  299:     CredsAlice = make_creds(Alice),
  300:     CredsBob = make_creds(Bob),
  301:     AliceBin = escalus_client:short_jid(Alice),
  302:     BobBin = escalus_client:short_jid(Bob),
  303:     RoomName = <<"first room">>,
  304:     {ok, #{jid := RoomJID}} = create_room(MUCServer, RoomName, <<"subject">>, AliceBin),
  305:     {ok, _} = invite_user(RoomJID, AliceBin, BobBin),
  306:     % Member kicks himself from a room
  307:     ?assertEqual(2, length(get_room_aff(RoomJID))),
  308:     Res = execute(Ep, user_kick_user_body(jid:to_binary(RoomJID), null), CredsBob),
  309:     ?assertNotEqual(nomatch, binary:match(get_ok_value(?KICK_USER_PATH, Res),
  310:                                           <<"successfully">>)),
  311:     ?assertEqual(1, length(get_room_aff(RoomJID))),
  312:     % Member cannot kick the room owner. The kick stanza is sent successfully,
  313:     % but is ignored by server
  314:     {ok, _} = invite_user(RoomJID, AliceBin, BobBin),
  315:     Res2 = execute(Ep, user_kick_user_body(jid:to_binary(RoomJID), AliceBin), CredsBob),
  316:     ?assertNotEqual(nomatch, binary:match(get_ok_value(?KICK_USER_PATH, Res2), <<"successfully">>)),
  317:     ?assertEqual(2, length(get_room_aff(RoomJID))),
  318:     % Owner kicks the member from a room
  319:     {ok, _} = invite_user(RoomJID, AliceBin, BobBin),
  320:     Res3 = execute(Ep, user_kick_user_body(jid:to_binary(RoomJID), BobBin), CredsAlice),
  321:     ?assertNotEqual(nomatch, binary:match(get_ok_value(?KICK_USER_PATH, Res3), <<"successfully">>)),
  322:     ?assertEqual(1, length(get_room_aff(RoomJID))).
  323: 
  324: user_send_message_to_room(Config) ->
  325:     escalus:fresh_story_with_config(Config, [{alice, 1}, {bob, 1}],
  326:                                     fun user_send_message_to_room_story/3).
  327: 
  328: user_send_message_to_room_story(Config, Alice, Bob) ->
  329:     Ep = ?config(schema_endpoint, Config),
  330:     MUCServer = ?config(muc_light_host, Config),
  331:     CredsAlice = make_creds(Alice),
  332:     AliceBin = escalus_client:short_jid(Alice),
  333:     BobBin = escalus_client:short_jid(Bob),
  334:     RoomName = <<"first room">>,
  335:     MsgBody = <<"Hello there!">>,
  336:     {ok, #{jid := RoomJID}} = create_room(MUCServer, RoomName, <<"subject">>, AliceBin),
  337:     {ok, _} = invite_user(RoomJID, AliceBin, BobBin),
  338:     Res = execute(Ep, user_send_message_to_room_body(jid:to_binary(RoomJID), MsgBody), CredsAlice),
  339:     ?assertNotEqual(nomatch, binary:match(get_ok_value(?SEND_MESSAGE_PATH, Res), <<"successfully">>)),
  340:     [_, Msg] = escalus:wait_for_stanzas(Bob, 2),
  341:     escalus:assert(is_message, Msg).
  342: 
  343: user_send_message_to_room_errors(Config) ->
  344:     escalus:fresh_story_with_config(Config, [{alice, 1}, {bob, 1}],
  345:                                     fun user_send_message_to_room_errors_story/3).
  346: 
  347: user_send_message_to_room_errors_story(Config, Alice, Bob) ->
  348:     Ep = ?config(schema_endpoint, Config),
  349:     MUCServer = ?config(muc_light_host, Config),
  350:     CredsAlice = make_creds(Alice),
  351:     CredsBob = make_creds(Bob),
  352:     AliceBin = escalus_client:short_jid(Alice),
  353:     BobBin = escalus_client:short_jid(Bob),
  354:     MsgBody = <<"Hello there!">>,
  355:     {ok, #{jid := #jid{luser = ARoomID} = ARoomJID}} =
  356:         create_room(MUCServer, <<"alice room">>, <<"subject">>, AliceBin),
  357:     % Try with a non-existent domain
  358:     Res = execute(Ep, user_send_message_to_room_body(
  359:                         make_bare_jid(ARoomID, ?UNKNOWN_DOMAIN), MsgBody), CredsAlice),
  360:     ?assertNotEqual(nomatch, binary:match(get_err_msg(Res), <<"not found">>)),
  361:     % Try with a user without rooms
  362:     Res2 = execute(Ep, user_send_message_to_room_body(
  363:                          jid:to_binary(ARoomJID), MsgBody), CredsBob),
  364:     ?assertNotEqual(nomatch, binary:match(get_err_msg(Res2), <<"not occupy this room">>)),
  365:     % Try with a room not occupied by this user
  366:     {ok, #{jid := _RoomJID2}} = create_room(MUCServer, <<"bob room">>, <<"subject">>, BobBin),
  367:     Res3 = execute(Ep, user_send_message_to_room_body(
  368:                          jid:to_binary(ARoomJID), MsgBody), CredsBob),
  369:     ?assertNotEqual(nomatch, binary:match(get_err_msg(Res3), <<"not occupy this room">>)).
  370: 
  371: user_get_room_messages(Config) ->
  372:     escalus:fresh_story_with_config(Config, [{alice, 1}, {bob, 1}], fun user_get_room_messages_story/3).
  373: 
  374: user_get_room_messages_story(Config, Alice, Bob) ->
  375:     Ep = ?config(schema_endpoint, Config),
  376:     MUCServer = ?config(muc_light_host, Config),
  377:     CredsAlice = make_creds(Alice),
  378:     CredsBob = make_creds(Bob),
  379:     AliceBin = escalus_client:short_jid(Alice),
  380:     {ok, #{jid := #jid{luser = RoomID} = RoomJID}} =
  381:         create_room(MUCServer, <<"first room">>, <<"subject">>, AliceBin),
  382:     Message = <<"Hello friends">>,
  383:     send_message_to_room(RoomJID, jid:from_binary(AliceBin), Message),
  384:     mam_helper:maybe_wait_for_archive(Config),
  385:     % Get messages so far
  386:     Limit = 40,
  387:     Res = execute(Ep, get_room_messages_body(jid:to_binary(RoomJID), Limit, null), CredsAlice),
  388:     #{<<"stanzas">> :=[#{<<"stanza">> := StanzaXML}], <<"limit">> := Limit} =
  389:         get_ok_value(?GET_MESSAGES_PATH, Res),
  390:     ?assertMatch({ok, #xmlel{name = <<"message">>}}, exml:parse(StanzaXML)),
  391:     % Get messages before the given date and time
  392:     Before = <<"2022-02-17T04:54:13+00:00">>,
  393:     Res2 = execute(Ep, get_room_messages_body(jid:to_binary(RoomJID), null, Before), CredsAlice),
  394:     ?assertMatch(#{<<"stanzas">> := [], <<"limit">> := 50}, get_ok_value(?GET_MESSAGES_PATH, Res2)),
  395:     % Try to pass too big page size value
  396:     Res3 = execute(Ep, get_room_messages_body(jid:to_binary(RoomJID), 51, Before), CredsAlice),
  397:     ?assertMatch(#{<<"limit">> := 50}, get_ok_value(?GET_MESSAGES_PATH, Res3)),
  398:     % Try with a non-existent domain
  399:     Res4 = execute(Ep, get_room_messages_body(
  400:                           make_bare_jid(RoomID, ?UNKNOWN_DOMAIN), Limit, null), CredsAlice),
  401:     ?assertNotEqual(nomatch, binary:match(get_err_msg(Res4), <<"not found">>)),
  402:     % Try with a user that is not a room member
  403:     Res5 = execute(Ep, get_room_messages_body(
  404:                           jid:to_binary(RoomJID), Limit, null), CredsBob),
  405:     ?assertNotEqual(nomatch, binary:match(get_err_msg(Res5), <<"not occupy this room">>)).
  406: 
  407: user_list_rooms(Config) ->
  408:     escalus:fresh_story_with_config(Config, [{alice, 1}], fun user_list_rooms_story/2).
  409: 
  410: user_list_rooms_story(Config, Alice) ->
  411:     Ep = ?config(schema_endpoint, Config),
  412:     MUCServer = ?config(muc_light_host, Config),
  413:     CredsAlice = make_creds(Alice),
  414:     AliceBin = escalus_client:short_jid(Alice),
  415:     {ok, #{jid := RoomJID}} = create_room(MUCServer, <<"room a">>, <<"subject">>, AliceBin),
  416:     {ok, #{jid := RoomJID2}} = create_room(MUCServer, <<"room b">>, <<"subject">>, AliceBin),
  417:     Res = execute(Ep, user_list_rooms_body(), CredsAlice),
  418:     ?assertEqual(lists:sort([jid:to_binary(RoomJID), jid:to_binary(RoomJID2)]),
  419:                  lists:sort(get_ok_value(?USER_LIST_ROOMS_PATH, Res))).
  420: 
  421: user_list_room_users(Config) ->
  422:     escalus:fresh_story_with_config(Config, [{alice, 1}, {bob, 1}], fun user_list_room_users_story/3).
  423: 
  424: user_list_room_users_story(Config, Alice, Bob) ->
  425:     Ep = ?config(schema_endpoint, Config),
  426:     MUCServer = ?config(muc_light_host, Config),
  427:     CredsAlice = make_creds(Alice),
  428:     CredsBob = make_creds(Bob),
  429:     AliceBin = escalus_client:short_jid(Alice),
  430:     BobBin = escalus_client:short_jid(Bob),
  431:     AliceLower = escalus_utils:jid_to_lower(AliceBin),
  432:     {ok, #{jid := RoomJID}} = create_room(MUCServer, <<"room a">>, <<"subject">>, AliceBin),
  433:     % Owner can list room users
  434:     Res = execute(Ep, list_room_users_body(jid:to_binary(RoomJID)), CredsAlice),
  435:     ?assertEqual([#{<<"jid">> => AliceLower, <<"affiliation">> => <<"OWNER">>}],
  436:                  get_ok_value(?LIST_ROOM_USERS_PATH, Res)),
  437:     % Try with a non-existent domain
  438:     Res2 = execute(Ep, list_room_users_body(
  439:                          make_bare_jid(RoomJID#jid.luser, ?UNKNOWN_DOMAIN)), CredsAlice),
  440:     ?assertNotEqual(nomatch, binary:match(get_err_msg(Res2), <<"not found">>)),
  441:     % Try with a non-existent room
  442:     Res3 = execute(Ep, list_room_users_body(
  443:                          make_bare_jid(?UNKNOWN, MUCServer)), CredsAlice),
  444:     ?assertNotEqual(nomatch, binary:match(get_err_msg(Res3), <<"not found">>)),
  445:     % User that is not a member cannot get aff
  446:     Res4 = execute(Ep, list_room_users_body(jid:to_binary(RoomJID)), CredsBob),
  447:     ?assertNotEqual(nomatch, binary:match(get_err_msg(Res4), <<"not occupy this room">>)),
  448:     % Member can get aff
  449:     {ok, _} = invite_user(RoomJID, AliceBin, BobBin),
  450:     escalus:wait_for_stanza(Bob),
  451:     Res5 = execute(Ep, list_room_users_body(jid:to_binary(RoomJID)), CredsBob),
  452:     ?assertMatch([_, _], get_ok_value(?LIST_ROOM_USERS_PATH, Res5)).
  453: 
  454: user_get_room_config(Config) ->
  455:     escalus:fresh_story_with_config(Config, [{alice, 1}, {bob, 1}], fun user_get_room_config_story/3).
  456: 
  457: user_get_room_config_story(Config, Alice, Bob) ->
  458:     Ep = ?config(schema_endpoint, Config),
  459:     MUCServer = ?config(muc_light_host, Config),
  460:     CredsAlice = make_creds(Alice),
  461:     CredsBob = make_creds(Bob),
  462:     AliceBin = escalus_client:short_jid(Alice),
  463:     BobBin = escalus_client:short_jid(Bob),
  464:     AliceLower = escalus_utils:jid_to_lower(AliceBin),
  465:     BobLower = escalus_utils:jid_to_lower(BobBin),
  466:     RoomName = <<"first room">>,
  467:     RoomSubject = <<"Room about nothing">>,
  468:     {ok, #{jid := #jid{luser = RoomID} = RoomJID}} =
  469:         create_room(MUCServer, RoomName, RoomSubject, AliceBin),
  470:     RoomJIDBin = jid:to_binary(RoomJID),
  471:     Res = execute(Ep, get_room_config_body(jid:to_binary(RoomJID)), CredsAlice),
  472:     % Owner can get a config
  473:     ?assertEqual(#{<<"jid">> => RoomJIDBin, <<"subject">> => RoomSubject, <<"name">> => RoomName,
  474:                     <<"participants">> => [#{<<"jid">> => AliceLower,
  475:                                              <<"affiliation">> => <<"OWNER">>}]},
  476:                  get_ok_value(?GET_ROOM_CONFIG_PATH, Res)),
  477:     % Try with a non-existent domain
  478:     Res2 = execute(Ep, get_room_config_body(make_bare_jid(RoomID, ?UNKNOWN_DOMAIN)), CredsAlice),
  479:     ?assertNotEqual(nomatch, binary:match(get_err_msg(Res2), <<"not found">>)),
  480:     % Try with a non-existent room
  481:     Res3 = execute(Ep, get_room_config_body(make_bare_jid(?UNKNOWN, MUCServer)), CredsAlice),
  482:     ?assertNotEqual(nomatch, binary:match(get_err_msg(Res3), <<"not found">>)),
  483:     % User that is not a member cannot get a room config
  484:     Res4 = execute(Ep, get_room_config_body(jid:to_binary(RoomJID)), CredsBob),
  485:     ?assertNotEqual(nomatch, binary:match(get_err_msg(Res4), <<"not occupy this room">>)),
  486:     % Member can get a config
  487:     {ok, _} = invite_user(RoomJID, AliceBin, BobBin),
  488:     Res5 = execute(Ep, get_room_config_body(jid:to_binary(RoomJID)), CredsBob),
  489:     ?assertEqual(#{<<"jid">> => RoomJIDBin, <<"subject">> => RoomSubject, <<"name">> => RoomName,
  490:                     <<"participants">> => [#{<<"jid">> => AliceLower,
  491:                                              <<"affiliation">> => <<"OWNER">>},
  492:                                            #{<<"jid">> => BobLower,
  493:                                              <<"affiliation">> => <<"MEMBER">>}]},
  494:                  get_ok_value(?GET_ROOM_CONFIG_PATH, Res5)).
  495: 
  496: user_blocking_list(Config) ->
  497:     escalus:fresh_story_with_config(Config, [{alice, 1}, {bob, 1}], fun user_blocking_list_story/3).
  498: 
  499: user_blocking_list_story(Config, Alice, Bob) ->
  500:     Ep = ?config(schema_endpoint, Config),
  501:     CredsAlice = make_creds(Alice),
  502:     BobBin = escalus_client:full_jid(Bob),
  503:     BobShortBin = escalus_utils:jid_to_lower(escalus_client:short_jid(Bob)),
  504:     {ok, #{jid := RoomJID}} = create_room(?config(muc_light_host, Config),
  505:                                           <<"room">>, <<"subject">>, BobBin),
  506:     RoomBin = jid:to_binary(RoomJID),
  507:     Res = execute(Ep, user_get_blocking_body(), CredsAlice),
  508:     ?assertMatch([], get_ok_value(?GET_BLOCKING_LIST_PATH, Res)),
  509:     Res2 = execute(Ep, user_set_blocking_body([{<<"USER">>, <<"DENY">>, BobBin}]), CredsAlice),
  510:     ?assertNotEqual(nomatch, binary:match(get_ok_value(?SET_BLOCKING_LIST_PATH, Res2),
  511:                                           <<"successfully">>)),
  512:     Res3 = execute(Ep, user_get_blocking_body(), CredsAlice),
  513:     ?assertEqual([#{<<"entityType">> => <<"USER">>,
  514:                     <<"action">> => <<"DENY">>,
  515:                     <<"entity">> => BobShortBin}],
  516:                  get_ok_value(?GET_BLOCKING_LIST_PATH, Res3)),
  517:     Res4 = execute(Ep, user_set_blocking_body([{<<"USER">>, <<"ALLOW">>, BobBin},
  518:                                                {<<"ROOM">>, <<"DENY">>, jid:to_binary(RoomJID)}]),
  519:                    CredsAlice),
  520:     ?assertNotEqual(nomatch, binary:match(get_ok_value(?SET_BLOCKING_LIST_PATH, Res4),
  521:                                           <<"successfully">>)),
  522:     Res5 = execute(Ep, user_get_blocking_body(), CredsAlice),
  523:     ?assertEqual([#{<<"entityType">> => <<"ROOM">>,
  524:                     <<"action">> => <<"DENY">>,
  525:                     <<"entity">> => RoomBin}],
  526:                  get_ok_value(?GET_BLOCKING_LIST_PATH, Res5)).
  527: 
  528: %% Admin test cases
  529: 
  530: admin_blocking_list(Config) ->
  531:     escalus:fresh_story_with_config(Config, [{alice, 1}, {bob, 1}], fun admin_blocking_list_story/3).
  532: 
  533: admin_blocking_list_story(Config, Alice, Bob) ->
  534:     AliceBin = escalus_client:full_jid(Alice),
  535:     BobBin = escalus_client:full_jid(Bob),
  536:     BobShortBin = escalus_utils:jid_to_lower(escalus_client:short_jid(Bob)),
  537:     Res = execute_auth(admin_get_user_blocking_body(AliceBin), Config),
  538:     ?assertMatch([], get_ok_value(?GET_BLOCKING_LIST_PATH, Res)),
  539:     Res2 = execute_auth(admin_set_blocking_body(
  540:                           AliceBin, [{<<"USER">>, <<"DENY">>, BobBin}]), Config),
  541:     ?assertNotEqual(nomatch, binary:match(get_ok_value(?SET_BLOCKING_LIST_PATH, Res2),
  542:                                           <<"successfully">>)),
  543:     Res3 = execute_auth(admin_get_user_blocking_body(AliceBin), Config),
  544:     ?assertEqual([#{<<"entityType">> => <<"USER">>,
  545:                     <<"action">> => <<"DENY">>,
  546:                     <<"entity">> => BobShortBin}],
  547:                  get_ok_value(?GET_BLOCKING_LIST_PATH, Res3)),
  548:     Res4 = execute_auth(admin_set_blocking_body(
  549:                           AliceBin, [{<<"USER">>, <<"ALLOW">>, BobBin}]), Config),
  550:     ?assertNotEqual(nomatch, binary:match(get_ok_value(?SET_BLOCKING_LIST_PATH, Res4),
  551:                                           <<"successfully">>)),
  552:     Res5 = execute_auth(admin_get_user_blocking_body(AliceBin), Config),
  553:     ?assertMatch([], get_ok_value(?GET_BLOCKING_LIST_PATH, Res5)),
  554:     % Check whether errors are handled correctly
  555:     InvalidUser = make_bare_jid(?UNKNOWN, ?UNKNOWN_DOMAIN),
  556:     Res6 = execute_auth(admin_get_user_blocking_body(InvalidUser), Config),
  557:     ?assertNotEqual(nomatch, binary:match(get_err_msg(Res6), <<"not found">>)),
  558:     Res7 = execute_auth(admin_set_blocking_body(InvalidUser, []), Config),
  559:     ?assertNotEqual(nomatch, binary:match(get_err_msg(Res7), <<"not found">>)).
  560: 
  561: admin_create_room(Config) ->
  562:     escalus:fresh_story_with_config(Config, [{alice, 1}], fun admin_create_room_story/2).
  563: 
  564: admin_create_room_story(Config, Alice) ->
  565:     AliceBin = escalus_client:short_jid(Alice),
  566:     AliceBinLower = escalus_utils:jid_to_lower(AliceBin),
  567:     MucServer = ?config(muc_light_host, Config),
  568:     Name = <<"first room">>,
  569:     Subject = <<"testing">>,
  570:     Res = execute_auth(admin_create_room_body(MucServer, Name, AliceBin, Subject, null), Config),
  571:     #{<<"jid">> := JID, <<"name">> := Name, <<"subject">> := Subject,
  572:       <<"participants">> := Participants} = get_ok_value(?CREATE_ROOM_PATH, Res),
  573:     ?assertMatch(#jid{server = MucServer}, jid:from_binary(JID)),
  574:     ?assertEqual([#{<<"jid">> => AliceBinLower, <<"affiliation">> => <<"OWNER">>}], Participants),
  575:     % Try with a non-existent domain
  576:     Res2 = execute_auth(admin_create_room_body(?UNKNOWN_DOMAIN, Name, AliceBin, Subject, null),
  577:                         Config),
  578:     ?assertNotEqual(nomatch, binary:match(get_err_msg(Res2), <<"not found">>)).
  579: 
  580: admin_create_identified_room(Config) ->
  581:     escalus:fresh_story_with_config(Config, [{alice, 1}], fun admin_create_identified_room_story/2).
  582: 
  583: admin_create_identified_room_story(Config, Alice) ->
  584:     AliceBin = escalus_client:short_jid(Alice),
  585:     MucServer = ?config(muc_light_host, Config),
  586:     Name = <<"first room">>,
  587:     Subject = <<"testing">>,
  588:     Id = <<"my_room">>,
  589:     Res = execute_auth(admin_create_room_body(MucServer, Name, AliceBin, Subject, Id), Config),
  590:     #{<<"jid">> := JID, <<"name">> := Name, <<"subject">> := Subject} =
  591:         get_ok_value(?CREATE_ROOM_PATH, Res),
  592:     ?assertMatch(#jid{user = Id, server = MucServer}, jid:from_binary(JID)),
  593:     % Create a room with an existing ID
  594:     Res2 = execute_auth(admin_create_room_body(MucServer, <<"snd room">>, AliceBin, Subject, Id),
  595:                         Config),
  596:     ?assertNotEqual(nomatch, binary:match(get_err_msg(Res2), <<"already exists">>)),
  597:     % Try with a non-existent domain
  598:     Res3 = execute_auth(admin_create_room_body(?UNKNOWN_DOMAIN, <<"name">>, AliceBin, Subject, Id),
  599:                         Config),
  600:     ?assertNotEqual(nomatch, binary:match(get_err_msg(Res3), <<"not found">>)),
  601:     % Try with an empty string passed as ID
  602:     Res4 = execute_auth(admin_create_room_body(MucServer, <<"name">>, AliceBin, Subject, <<>>), Config),
  603:     ?assertNotEqual(nomatch, binary:match(get_coertion_err_msg(Res4), <<"Given string is empty">>)).
  604: 
  605: admin_change_room_config(Config) ->
  606:     escalus:fresh_story_with_config(Config, [{alice, 1}], fun admin_change_room_config_story/2).
  607: 
  608: admin_change_room_config_story(Config, Alice) ->
  609:     AliceBin = escalus_client:short_jid(Alice),
  610:     MUCServer = ?config(muc_light_host, Config),
  611:     Name = <<"first room">>,
  612:     Subject = <<"testing">>,
  613:     % Create a new room
  614:     {ok, #{jid := RoomJID}} = create_room(MUCServer, Name, Subject, AliceBin),
  615:     % Try to change the room configuration
  616:     Name2 = <<"changed room">>,
  617:     Subject2 = <<"not testing">>,
  618:     Res = execute_auth(admin_change_room_configuration_body(jid:to_binary(RoomJID),
  619:                                                             AliceBin, Name2, Subject2), Config),
  620:     ?assertMatch(#{<<"name">> := Name2, <<"subject">> := Subject2},
  621:                  get_ok_value(?CHANGE_CONFIG_PATH, Res)).
  622: 
  623: admin_change_room_config_errors(Config) ->
  624:     escalus:fresh_story_with_config(Config, [{alice, 1}, {bob, 1}],
  625:                                     fun admin_change_room_config_errors_story/3).
  626: 
  627: admin_change_room_config_errors_story(Config, Alice, Bob) ->
  628:     AliceBin = escalus_client:short_jid(Alice),
  629:     BobBin = escalus_client:short_jid(Bob),
  630:     MUCServer = ?config(muc_light_host, Config),
  631:     RoomName = <<"first room">>,
  632:     {ok, #{jid := #jid{luser = RoomID} = RoomJID}} =
  633:         create_room(MUCServer, RoomName, <<"subject">>, AliceBin),
  634:     {ok, _} = invite_user(RoomJID, AliceBin, BobBin),
  635:     % Try to change the config with a non-existent domain
  636:     Res = execute_auth(admin_change_room_configuration_body(
  637:                          make_bare_jid(RoomID, ?UNKNOWN_DOMAIN), AliceBin, RoomName, <<"subject2">>), Config),
  638:     ?assertNotEqual(nomatch, binary:match(get_err_msg(Res), <<"not found">>)),
  639:     % Try to change the config of the non-existent room
  640:     Res2 = execute_auth(admin_change_room_configuration_body(
  641:                           make_bare_jid(<<"unknown">>, MUCServer), AliceBin,
  642:                           RoomName, <<"subject2">>), Config),
  643:     ?assertNotEqual(nomatch, binary:match(get_err_msg(Res2), <<"not found">>)),
  644:     % Try to change the config by the non-existent user
  645:     Res3 = execute_auth(admin_change_room_configuration_body(
  646:                           jid:to_binary(RoomJID), <<"wrong-user@wrong-domain">>,
  647:                           RoomName, <<"subject2">>), Config),
  648:     ?assertNotEqual(nomatch, binary:match(get_err_msg(Res3), <<"not occupy this room">>)),
  649:     % Try to change a config by the user without permission
  650:     Res4 = execute_auth(admin_change_room_configuration_body(
  651:                           jid:to_binary(RoomJID), BobBin, RoomName, <<"subject2">>), Config),
  652:     ?assertNotEqual(nomatch, binary:match(get_err_msg(Res4),
  653:                                           <<"does not have permission to change">>)).
  654: 
  655: admin_invite_user(Config) ->
  656:     escalus:fresh_story_with_config(Config, [{alice, 1}, {bob, 1}], fun admin_invite_user_story/3).
  657: 
  658: admin_invite_user_story(Config, Alice, Bob) ->
  659:     AliceBin = escalus_client:short_jid(Alice),
  660:     BobBin = escalus_client:short_jid(Bob),
  661:     Domain = escalus_client:server(Alice),
  662:     MUCServer = ?config(muc_light_host, Config),
  663:     Name = <<"first room">>,
  664:     {ok, #{jid := RoomJID}} = create_room(MUCServer, Name, <<"subject2">>, AliceBin),
  665: 
  666:     Res = execute_auth(admin_invite_user_body(jid:to_binary(RoomJID), AliceBin, BobBin), Config),
  667:     ?assertNotEqual(nomatch, binary:match(get_ok_value(?INVITE_USER_PATH, Res),
  668:                                           <<"successfully">>)),
  669:     BobName = escalus_utils:jid_to_lower(escalus_client:username(Bob)),
  670:     AliceName = escalus_utils:jid_to_lower(escalus_client:username(Alice)),
  671:     ExpectedAff = lists:sort([{{AliceName, Domain}, owner},
  672:                               {{BobName, Domain}, member}]),
  673:     ?assertMatch(ExpectedAff, lists:sort(get_room_aff(RoomJID))).
  674: 
  675: admin_invite_user_errors(Config) ->
  676:     escalus:fresh_story_with_config(Config, [{alice, 1}, {bob, 1}],
  677:                                     fun admin_invite_user_errors_story/3).
  678: 
  679: admin_invite_user_errors_story(Config, Alice, Bob) ->
  680:     AliceBin = escalus_client:short_jid(Alice),
  681:     BobBin = escalus_client:short_jid(Bob),
  682:     MUCServer = ?config(muc_light_host, Config),
  683:     {ok, #{jid := #jid{luser = RoomID} = RoomJID}} =
  684:         create_room(MUCServer, <<"first room">>, <<"subject">>, AliceBin),
  685:     % Try to invite a user to not existing room
  686:     Res = execute_auth(admin_invite_user_body(
  687:                          make_bare_jid(?UNKNOWN, MUCServer), AliceBin, BobBin), Config),
  688:     ?assertNotEqual(nomatch, binary:match(get_err_msg(Res), <<"does not occupy this room">>)),
  689:     % User without rooms tries to invite a user
  690:     Res2 = execute_auth(admin_invite_user_body(
  691:                           jid:to_binary(RoomJID), BobBin, AliceBin), Config),
  692:     ?assertNotEqual(nomatch, binary:match(get_err_msg(Res2), <<"does not occupy this room">>)),
  693:     % Try with a non-existent domain
  694:     Res3 = execute_auth(admin_invite_user_body(
  695:                           make_bare_jid(RoomID, ?UNKNOWN_DOMAIN), AliceBin, BobBin), Config),
  696:     ?assertNotEqual(nomatch, binary:match(get_err_msg(Res3), <<"not found">>)).
  697: 
  698: admin_delete_room(Config) ->
  699:     escalus:fresh_story_with_config(Config, [{alice, 1}], fun admin_delete_room_story/2).
  700: 
  701: admin_delete_room_story(Config, Alice) ->
  702:     AliceBin = escalus_client:short_jid(Alice),
  703:     MUCServer = ?config(muc_light_host, Config),
  704:     Name = <<"first room">>,
  705:     {ok, #{jid := #jid{luser = RoomID} = RoomJID}} =
  706:         create_room(MUCServer, Name, <<"subject">>, AliceBin),
  707:     Res = execute_auth(delete_room_body(jid:to_binary(RoomJID)), Config),
  708:     ?assertNotEqual(nomatch, binary:match(get_ok_value(?DELETE_ROOM_PATH, Res),
  709:                                           <<"successfully">>)),
  710:     ?assertEqual({error, not_exists}, get_room_info(jid:from_binary(RoomJID))),
  711:     % Try with a non-existent domain
  712:     Res2 = execute_auth(delete_room_body(make_bare_jid(RoomID, ?UNKNOWN_DOMAIN)), Config),
  713:     ?assertNotEqual(nomatch, binary:match(get_err_msg(Res2), <<"not found">>)),
  714:     % Try with a non-existent room
  715:     Res3 = execute_auth(delete_room_body(make_bare_jid(?UNKNOWN, MUCServer)), Config),
  716:     ?assertNotEqual(nomatch, binary:match(get_err_msg(Res3), <<"not found">>)).
  717: 
  718: admin_kick_user(Config) ->
  719:     escalus:fresh_story_with_config(Config, [{alice, 1}, {bob, 1}], fun admin_kick_user_story/3).
  720: 
  721: admin_kick_user_story(Config, Alice, Bob) ->
  722:     AliceBin = escalus_client:short_jid(Alice),
  723:     BobBin = escalus_client:short_jid(Bob),
  724:     MUCServer = ?config(muc_light_host, Config),
  725:     RoomName = <<"first room">>,
  726:     {ok, #{jid := RoomJID}} = create_room(MUCServer, RoomName, <<"subject">>, AliceBin),
  727:     {ok, _} = invite_user(RoomJID, AliceBin, BobBin),
  728:     ?assertEqual(2, length(get_room_aff(RoomJID))),
  729:     Res = execute_auth(admin_kick_user_body(jid:to_binary(RoomJID), BobBin), Config),
  730:     ?assertNotEqual(nomatch, binary:match(get_ok_value(?KICK_USER_PATH, Res),
  731:                                           <<"successfully">>)),
  732:     ?assertEqual(1, length(get_room_aff(RoomJID))).
  733: 
  734: admin_send_message_to_room(Config) ->
  735:     escalus:fresh_story_with_config(Config, [{alice, 1}, {bob, 1}],
  736:                                     fun admin_send_message_to_room_story/3).
  737: 
  738: admin_send_message_to_room_story(Config, Alice, Bob) ->
  739:     AliceBin = escalus_client:short_jid(Alice),
  740:     BobBin = escalus_client:short_jid(Bob),
  741:     MUCServer = ?config(muc_light_host, Config),
  742:     RoomName = <<"first room">>,
  743:     MsgBody = <<"Hello there!">>,
  744:     {ok, #{jid := RoomJID}} = create_room(MUCServer, RoomName, <<"subject">>, AliceBin),
  745:     {ok, _} = invite_user(RoomJID, AliceBin, BobBin),
  746:     Res = execute_auth(admin_send_message_to_room_body(
  747:                          jid:to_binary(RoomJID), AliceBin, MsgBody), Config),
  748:     ?assertNotEqual(nomatch, binary:match(get_ok_value(?SEND_MESSAGE_PATH, Res),
  749:                                           <<"successfully">>)),
  750:     [_, Msg] = escalus:wait_for_stanzas(Bob, 2),
  751:     escalus:assert(is_message, Msg).
  752: 
  753: admin_send_message_to_room_errors(Config) ->
  754:     escalus:fresh_story_with_config(Config, [{alice, 1}, {bob, 1}],
  755:                                     fun admin_send_message_to_room_errors_story/3).
  756: 
  757: admin_send_message_to_room_errors_story(Config, Alice, Bob) ->
  758:     AliceBin = escalus_client:short_jid(Alice),
  759:     BobBin = escalus_client:short_jid(Bob),
  760:     MUCServer = ?config(muc_light_host, Config),
  761:     MsgBody = <<"Hello there!">>,
  762:     {ok, #{jid := #jid{luser = ARoomID} = ARoomJID}} =
  763:         create_room(MUCServer, <<"alice room">>, <<"subject">>, AliceBin),
  764:     % Try with a non-existent domain
  765:     Res2 = execute_auth(admin_send_message_to_room_body(
  766:                           make_bare_jid(ARoomID, ?UNKNOWN_DOMAIN), AliceBin, MsgBody), Config),
  767:     ?assertNotEqual(nomatch, binary:match(get_err_msg(Res2), <<"not found">>)),
  768:     % Try with a user without rooms
  769:     Res3 = execute_auth(admin_send_message_to_room_body(
  770:                           jid:to_binary(ARoomJID), BobBin, MsgBody), Config),
  771:     ?assertNotEqual(nomatch, binary:match(get_err_msg(Res3), <<"does not occupy this room">>)),
  772:     % Try with a room not occupied by this user
  773:     {ok, #{jid := _RoomJID2}} = create_room(MUCServer, <<"bob room">>, <<"subject">>, BobBin),
  774:     Res4 = execute_auth(admin_send_message_to_room_body(
  775:                           jid:to_binary(ARoomJID), BobBin, MsgBody), Config),
  776:     ?assertNotEqual(nomatch, binary:match(get_err_msg(Res4), <<"does not occupy this room">>)).
  777: 
  778: admin_get_room_messages(Config) ->
  779:     escalus:fresh_story_with_config(Config, [{alice, 1}], fun admin_get_room_messages_story/2).
  780: 
  781: admin_get_room_messages_story(Config, Alice) ->
  782:     AliceBin = escalus_client:short_jid(Alice),
  783:     %Domain = escalus_client:server(Alice),
  784:     MUCServer = ?config(muc_light_host, Config),
  785:     RoomName = <<"first room">>,
  786:     RoomName2 = <<"second room">>,
  787:     {ok, #{jid := #jid{luser = RoomID} = RoomJID}} =
  788:         create_room(MUCServer, RoomName, <<"subject">>, AliceBin),
  789:     {ok, _} = create_room(MUCServer, RoomName2, <<"subject">>, AliceBin),
  790:     Message = <<"Hello friends">>,
  791:     send_message_to_room(RoomJID, jid:from_binary(AliceBin), Message),
  792:     mam_helper:maybe_wait_for_archive(Config),
  793:     % Get messages so far
  794:     Limit = 40,
  795:     Res = execute_auth(get_room_messages_body(jid:to_binary(RoomJID), Limit, null), Config),
  796:     #{<<"stanzas">> := [#{<<"stanza">> := StanzaXML}], <<"limit">> := Limit} =
  797:         get_ok_value(?GET_MESSAGES_PATH, Res),
  798:     ?assertMatch({ok, #xmlel{name = <<"message">>}}, exml:parse(StanzaXML)),
  799:     % Get messages before the given date and time
  800:     Before = <<"2022-02-17T04:54:13+00:00">>,
  801:     Res2 = execute_auth(get_room_messages_body(jid:to_binary(RoomJID), null, Before), Config),
  802:     ?assertMatch(#{<<"stanzas">> := [], <<"limit">> := 50}, get_ok_value(?GET_MESSAGES_PATH, Res2)),
  803:     % Try to pass too big page size value
  804:     Res3 = execute_auth(get_room_messages_body(jid:to_binary(RoomJID), 51, Before), Config),
  805:     ?assertMatch(#{<<"limit">> := 50},get_ok_value(?GET_MESSAGES_PATH, Res3)),
  806:     % Try with a non-existent domain
  807:     Res4 = execute_auth(get_room_messages_body(
  808:                           make_bare_jid(RoomID, ?UNKNOWN_DOMAIN), Limit, null), Config),
  809:     ?assertNotEqual(nomatch, binary:match(get_err_msg(Res4), <<"not found">>)).
  810: 
  811: admin_list_user_rooms(Config) ->
  812:     escalus:fresh_story_with_config(Config, [{alice, 1}], fun admin_list_user_rooms_story/2).
  813: 
  814: admin_list_user_rooms_story(Config, Alice) ->
  815:     AliceBin = escalus_client:short_jid(Alice),
  816:     Domain = escalus_client:server(Alice),
  817:     MUCServer = ?config(muc_light_host, Config),
  818:     RoomName = <<"first room">>,
  819:     RoomName2 = <<"second room">>,
  820:     {ok, #{jid := RoomJID}} = create_room(MUCServer, RoomName, <<"subject">>, AliceBin),
  821:     {ok, #{jid := RoomJID2}} = create_room(MUCServer, RoomName2, <<"subject">>, AliceBin),
  822:     Res = execute_auth(admin_list_user_rooms_body(AliceBin), Config),
  823:     ?assertEqual(lists:sort([jid:to_binary(RoomJID), jid:to_binary(RoomJID2)]),
  824:                  lists:sort(get_ok_value(?LIST_USER_ROOMS_PATH, Res))),
  825:     % Try with a non-existent user
  826:     Res2 = execute_auth(admin_list_user_rooms_body(<<"not-exist@", Domain/binary>>), Config),
  827:     ?assertEqual([], lists:sort(get_ok_value(?LIST_USER_ROOMS_PATH, Res2))),
  828:     % Try with a non-existent domain
  829:     Res3 = execute_auth(admin_list_user_rooms_body(<<"not-exist@not-exist">>), Config),
  830:     ?assertNotEqual(nomatch, binary:match(get_err_msg(Res3), <<"not found">>)).
  831: 
  832: admin_list_room_users(Config) ->
  833:     escalus:fresh_story_with_config(Config, [{alice, 1}], fun admin_list_room_users_story/2).
  834: 
  835: admin_list_room_users_story(Config, Alice) ->
  836:     AliceBin = escalus_client:short_jid(Alice),
  837:     AliceLower = escalus_utils:jid_to_lower(AliceBin),
  838:     MUCServer = ?config(muc_light_host, Config),
  839:     RoomName = <<"first room">>,
  840:     {ok, #{jid := RoomJID}} = create_room(MUCServer, RoomName, <<"subject">>, AliceBin),
  841:     Res = execute_auth(list_room_users_body(jid:to_binary(RoomJID)), Config),
  842:     ?assertEqual([#{<<"jid">> => AliceLower, <<"affiliation">> => <<"OWNER">>}],
  843:                  get_ok_value(?LIST_ROOM_USERS_PATH, Res)),
  844:     % Try with a non-existent domain
  845:     Res2 = execute_auth(list_room_users_body(
  846:                           make_bare_jid(RoomJID#jid.luser, ?UNKNOWN_DOMAIN)), Config),
  847:     ?assertNotEqual(nomatch, binary:match(get_err_msg(Res2), <<"not found">>)),
  848:     % Try with a non-existent room
  849:     Res3 = execute_auth(list_room_users_body(
  850:                           make_bare_jid(?UNKNOWN, MUCServer)), Config),
  851:     ?assertNotEqual(nomatch, binary:match(get_err_msg(Res3), <<"not found">>)).
  852: 
  853: admin_get_room_config(Config) ->
  854:     escalus:fresh_story_with_config(Config, [{alice, 1}], fun admin_get_room_config_story/2).
  855: 
  856: admin_get_room_config_story(Config, Alice) ->
  857:     AliceBin = escalus_client:short_jid(Alice),
  858:     AliceLower = escalus_utils:jid_to_lower(AliceBin),
  859:     MUCServer = ?config(muc_light_host, Config),
  860:     RoomName = <<"first room">>,
  861:     RoomSubject = <<"Room about nothing">>,
  862:     {ok, #{jid := #jid{luser = RoomID} = RoomJID}} =
  863:         create_room(MUCServer, RoomName, RoomSubject, AliceBin),
  864:     RoomJIDBin = jid:to_binary(RoomJID),
  865:     Res = execute_auth(get_room_config_body(jid:to_binary(RoomJID)), Config),
  866:     ?assertEqual(#{<<"jid">> => RoomJIDBin, <<"subject">> => RoomSubject, <<"name">> => RoomName,
  867:                     <<"participants">> => [#{<<"jid">> => AliceLower,
  868:                                              <<"affiliation">> => <<"OWNER">>}]},
  869:                  get_ok_value([data, muc_light, getRoomConfig], Res)),
  870:     % Try with a non-existent domain
  871:     Res2 = execute_auth(get_room_config_body(make_bare_jid(RoomID, ?UNKNOWN_DOMAIN)), Config),
  872:     ?assertNotEqual(nomatch, binary:match(get_err_msg(Res2), <<"not found">>)),
  873:     % Try with a non-existent room
  874:     Res3 = execute_auth(get_room_config_body(make_bare_jid(?UNKNOWN, MUCServer)), Config),
  875:     ?assertNotEqual(nomatch, binary:match(get_err_msg(Res3), <<"not found">>)).
  876: 
  877: %% Helpers
  878: 
  879: make_bare_jid(User, Server) ->
  880:     JID = jid:make_bare(User, Server),
  881:     jid:to_binary(JID).
  882: 
  883: send_message_to_room(RoomJID, SenderJID, Message) ->
  884:     rpc(mim(), mod_muc_light_api, send_message, [RoomJID, SenderJID, Message]).
  885: 
  886: get_room_messages(ID, Domain) ->
  887:     {ok, Messages} = rpc(mim(), mod_muc_light_api, get_room_messages, [Domain, ID]),
  888:     Messages.
  889: 
  890: create_room(Domain, Name, Subject, CreatorBin) ->
  891:     CreatorJID = jid:from_binary(CreatorBin),
  892:     rpc(mim(), mod_muc_light_api, create_room, [Domain, CreatorJID, Name, Subject]).
  893: 
  894: invite_user(RoomJID, SenderBin, RecipientBin) ->
  895:     SenderJID = jid:from_binary(SenderBin),
  896:     RecipientJID = jid:from_binary(RecipientBin),
  897:     rpc(mim(), mod_muc_light_api, invite_to_room, [RoomJID, SenderJID, RecipientJID]).
  898: 
  899: get_room_info(JID) ->
  900:     HostType = domain_helper:host_type(),
  901:     RoomUS = jid:to_lus(JID),
  902:     rpc(mim(), mod_muc_light_db_backend, get_info, [HostType, RoomUS]).
  903: 
  904: get_room_aff(JID) ->
  905:     {ok, _, Aff, _} = get_room_info(JID),
  906:     Aff. 
  907: 
  908: prepare_blocking_items_for_query(Items) ->
  909:     [#{<<"entity">> => Who, <<"entityType">> => What,
  910:        <<"action">> => Action} || {What, Action, Who} <- Items].
  911: 
  912: get_coertion_err_msg(Response) ->
  913:   {{<<"400">>, <<"Bad Request">>},
  914:    #{<<"errors">> := [#{<<"message">> := Msg,
  915:                         <<"extensions">> := #{<<"code">> := <<"input_coercion">>}}]}} = Response,
  916:     Msg.
  917: 
  918: %% Request bodies
  919: 
  920: admin_create_room_body(MUCDomain, Name, Owner, Subject, Id) ->
  921:     Query = <<"mutation M1($mucDomain: String!, $name: String!, $owner: JID!, $subject: String!, $id: NonEmptyString)
  922:               { muc_light { createRoom(mucDomain: $mucDomain, name: $name, owner: $owner, subject: $subject, id: $id)
  923:               { jid name subject participants {jid affiliation} } } }">>,
  924:     OpName = <<"M1">>,
  925:     Vars = #{<<"mucDomain">> => MUCDomain, <<"name">> => Name, <<"owner">> => Owner,
  926:              <<"subject">> => Subject, <<"id">> => Id},
  927:     #{query => Query, operationName => OpName, variables => Vars}.
  928: 
  929: admin_change_room_configuration_body(RoomJID, OwnerJID, Name, Subject) ->
  930:     Query = <<"mutation M1($room: JID!, $name: String!, $owner: JID!, $subject: String!)
  931:               { muc_light { changeRoomConfiguration(room: $room, name: $name, owner: $owner, subject: $subject)
  932:               { jid name subject participants {jid affiliation} } } }">>,
  933:     OpName = <<"M1">>,
  934:     Vars = #{<<"room">> => RoomJID, <<"name">> => Name, <<"owner">> => OwnerJID,
  935:              <<"subject">> => Subject},
  936:     #{query => Query, operationName => OpName, variables => Vars}.
  937: 
  938: admin_invite_user_body(RoomJID, Sender, Recipient) ->
  939:     Query = <<"mutation M1($room: JID!, $sender: JID!, $recipient: JID!)
  940:               { muc_light { inviteUser(room: $room, sender: $sender, recipient: $recipient) } }">>,
  941:     OpName = <<"M1">>,
  942:     Vars = #{<<"room">> => RoomJID, <<"sender">> => Sender, <<"recipient">> => Recipient},
  943:     #{query => Query, operationName => OpName, variables => Vars}.
  944: 
  945: admin_kick_user_body(RoomJID, User) ->
  946:     Query = <<"mutation M1($room: JID!, $user: JID!)
  947:               { muc_light { kickUser(room: $room, user: $user)} }">>,
  948:     OpName = <<"M1">>,
  949:     Vars = #{<<"room">> => RoomJID, <<"user">> => User},
  950:     #{query => Query, operationName => OpName, variables => Vars}.
  951: 
  952: admin_send_message_to_room_body(RoomJID, From, Body) ->
  953:     Query = <<"mutation M1($room: JID!, $from: JID!, $body: String!)
  954:               { muc_light { sendMessageToRoom(room: $room, from: $from, body: $body)} }">>,
  955:     OpName = <<"M1">>,
  956:     Vars = #{<<"room">> => RoomJID, <<"from">> => From, <<"body">> => Body},
  957:     #{query => Query, operationName => OpName, variables => Vars}.
  958: 
  959: admin_list_user_rooms_body(User) ->
  960:     Query = <<"query Q1($user: JID!)
  961:               { muc_light { listUserRooms(user: $user) } }">>,
  962:     OpName = <<"Q1">>,
  963:     Vars = #{<<"user">> => User},
  964:     #{query => Query, operationName => OpName, variables => Vars}.
  965: 
  966: user_create_room_body(MUCDomain, Name, Subject, Id) ->
  967:     Query = <<"mutation M1($mucDomain: String!, $name: String!, $subject: String!, $id: NonEmptyString)
  968:               { muc_light { createRoom(mucDomain: $mucDomain, name: $name, subject: $subject, id: $id)
  969:               { jid name subject participants {jid affiliation} } } }">>,
  970:     OpName = <<"M1">>,
  971:     Vars = #{<<"mucDomain">> => MUCDomain, <<"name">> => Name, <<"subject">> => Subject,
  972:              <<"id">> => Id},
  973:     #{query => Query, operationName => OpName, variables => Vars}.
  974: 
  975: user_change_room_configuration_body(RoomJID, Name, Subject) ->
  976:     Query = <<"mutation M1($room: JID!, $name: String!, $subject: String!)
  977:               { muc_light { changeRoomConfiguration(room: $room, name: $name, subject: $subject)
  978:               { jid name subject participants {jid affiliation} } } }">>,
  979:     OpName = <<"M1">>,
  980:     Vars = #{<<"room">> => RoomJID, <<"name">> => Name, <<"subject">> => Subject},
  981:     #{query => Query, operationName => OpName, variables => Vars}.
  982: 
  983: user_invite_user_body(RoomJID, Recipient) ->
  984:     Query = <<"mutation M1($room: JID!, $recipient: JID!)
  985:               { muc_light { inviteUser(room: $room, recipient: $recipient) } }">>,
  986:     OpName = <<"M1">>,
  987:     Vars = #{<<"room">> => RoomJID, <<"recipient">> => Recipient},
  988:     #{query => Query, operationName => OpName, variables => Vars}.
  989: 
  990: delete_room_body(RoomJID) ->
  991:     Query = <<"mutation M1($room: JID!)
  992:               { muc_light { deleteRoom(room: $room) } }">>,
  993:     OpName = <<"M1">>,
  994:     Vars = #{<<"room">> => RoomJID},
  995:     #{query => Query, operationName => OpName, variables => Vars}.
  996: 
  997: user_kick_user_body(RoomJID, User) ->
  998:     Query = <<"mutation M1($room: JID!, $user: JID)
  999:               { muc_light { kickUser(room: $room, user: $user)} }">>,
 1000:     OpName = <<"M1">>,
 1001:     Vars = #{<<"room">> => RoomJID, <<"user">> => User},
 1002:     #{query => Query, operationName => OpName, variables => Vars}.
 1003: 
 1004: user_send_message_to_room_body(RoomJID, Body) ->
 1005:     Query = <<"mutation M1($room: JID!, $body: String!)
 1006:               { muc_light { sendMessageToRoom(room: $room, body: $body)} }">>,
 1007:     OpName = <<"M1">>,
 1008:     Vars = #{<<"room">> => RoomJID, <<"body">> => Body},
 1009:     #{query => Query, operationName => OpName, variables => Vars}.
 1010: 
 1011: get_room_messages_body(RoomJID, PageSize, Before) ->
 1012:     Query = <<"query Q1($room: JID!, $pageSize: Int, $before: DateTime)
 1013:               { muc_light { getRoomMessages(room: $room, pageSize: $pageSize, before: $before)
 1014:               { stanzas { stanza } limit } } }">>,
 1015:     OpName = <<"Q1">>,
 1016:     Vars = #{<<"room">> => RoomJID, <<"pageSize">> => PageSize, <<"before">> => Before},
 1017:     #{query => Query, operationName => OpName, variables => Vars}.
 1018: 
 1019: user_list_rooms_body() ->
 1020:     Query = <<"query Q1 { muc_light { listRooms } }">>,
 1021:     #{query => Query, operationName => <<"Q1">>, variables => #{}}.
 1022: 
 1023: list_room_users_body(RoomJID) ->
 1024:     Query = <<"query Q1($room: JID!)
 1025:               { muc_light { listRoomUsers(room: $room)
 1026:               { jid affiliation} } }">>,
 1027:     OpName = <<"Q1">>,
 1028:     Vars = #{<<"room">> => RoomJID},
 1029:     #{query => Query, operationName => OpName, variables => Vars}.
 1030: 
 1031: get_room_config_body(RoomJID) ->
 1032:     Query = <<"query Q1($room: JID!)
 1033:               { muc_light { getRoomConfig(room: $room)
 1034:               { jid name subject participants {jid affiliation} } } }">>,
 1035:     OpName = <<"Q1">>,
 1036:     Vars = #{<<"room">> => RoomJID},
 1037:     #{query => Query, operationName => OpName, variables => Vars}.
 1038: 
 1039: admin_get_user_blocking_body(UserJID) ->
 1040:     Query = <<"query Q1($user: JID!)
 1041:               { muc_light { getBlockingList(user: $user)
 1042:               { entity entityType action } } }">>,
 1043:     OpName = <<"Q1">>,
 1044:     Vars = #{<<"user">> => UserJID},
 1045:     #{query => Query, operationName => OpName, variables => Vars}.
 1046: 
 1047: admin_set_blocking_body(UserJID, Items) ->
 1048:     Query = <<"mutation M1($user: JID!, $items: [BlockingInput!]!)
 1049:               { muc_light { setBlockingList(user: $user, items: $items) } }">>,
 1050:     OpName = <<"M1">>,
 1051:     Vars = #{<<"user">> => UserJID, <<"items">> => prepare_blocking_items_for_query(Items)},
 1052:     #{query => Query, operationName => OpName, variables => Vars}.
 1053: 
 1054: user_get_blocking_body() ->
 1055:     Query = <<"query Q1 { muc_light { getBlockingList { entity entityType action } } }">>,
 1056:     OpName = <<"Q1">>,
 1057:     #{query => Query, operationName => OpName}.
 1058: 
 1059: user_set_blocking_body(Items) ->
 1060:     Query = <<"mutation M1($items: [BlockingInput!]!)
 1061:               { muc_light { setBlockingList(items: $items) } }">>,
 1062:     OpName = <<"M1">>,
 1063:     Vars = #{<<"items">> => prepare_blocking_items_for_query(Items)},
 1064:     #{query => Query, operationName => OpName, variables => Vars}.