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