1: -module(domain_removal_SUITE).
    2: 
    3: -compile([export_all, nowarn_export_all]).
    4: 
    5: -import(distributed_helper, [mim/0, rpc/4, subhost_pattern/1]).
    6: -import(domain_helper, [host_type/0, domain_to_host_type/2, domain/0]).
    7: 
    8: -include("mam_helper.hrl").
    9: -include_lib("eunit/include/eunit.hrl").
   10: -include_lib("exml/include/exml_stream.hrl").
   11: -include_lib("jid/include/jid.hrl").
   12: -include_lib("common_test/include/ct.hrl").
   13: 
   14: all() ->
   15:     [{group, auth_removal},
   16:      {group, cache_removal},
   17:      {group, mam_removal},
   18:      {group, inbox_removal},
   19:      {group, muc_light_removal},
   20:      {group, muc_removal},
   21:      {group, private_removal},
   22:      {group, roster_removal},
   23:      {group, offline_removal},
   24:      {group, vcard_removal},
   25:      {group, last_removal}].
   26: 
   27: groups() ->
   28:     [
   29:      {auth_removal, [], [auth_removal]},
   30:      {cache_removal, [], [cache_removal]},
   31:      {mam_removal, [], [mam_pm_removal,
   32:                         mam_muc_removal]},
   33:      {inbox_removal, [], [inbox_removal]},
   34:      {muc_light_removal, [], [muc_light_removal,
   35:                               muc_light_blocking_removal]},
   36:      {muc_removal, [], [muc_removal]},
   37:      {private_removal, [], [private_removal]},
   38:      {roster_removal, [], [roster_removal]},
   39:      {offline_removal, [], [offline_removal]},
   40:      {vcard_removal, [], [vcard_removal]},
   41:      {last_removal, [], [last_removal]}
   42:     ].
   43: 
   44: %%%===================================================================
   45: %%% Overall setup/teardown
   46: %%%===================================================================
   47: init_per_suite(Config) ->
   48:     escalus:init_per_suite(Config).
   49: 
   50: end_per_suite(Config) ->
   51:     escalus_fresh:clean(),
   52:     escalus:end_per_suite(Config).
   53: 
   54: %%%===================================================================
   55: %%% Group specific setup/teardown
   56: %%%===================================================================
   57: init_per_group(Group, Config) ->
   58:     case mongoose_helper:is_rdbms_enabled(host_type()) of
   59:         true ->
   60:             HostTypes = domain_helper:host_types(),
   61:             Config2 = dynamic_modules:save_modules(HostTypes, Config),
   62:             [dynamic_modules:ensure_modules(HostType, group_to_modules(Group)) ||
   63:                 HostType <- HostTypes],
   64:             Config2;
   65:         false ->
   66:             {skip, require_rdbms}
   67:     end.
   68: 
   69: end_per_group(_Groupname, Config) ->
   70:     case mongoose_helper:is_rdbms_enabled(host_type()) of
   71:         true ->
   72:             dynamic_modules:restore_modules(Config);
   73:         false ->
   74:             ok
   75:     end,
   76:     ok.
   77: 
   78: group_to_modules(auth_removal) ->
   79:     [];
   80: group_to_modules(cache_removal) ->
   81:     [{mod_cache_users, []},
   82:      {mod_mam_meta, [{backend, rdbms}, {pm, []}]}];
   83: group_to_modules(mam_removal) ->
   84:     MucHost = subhost_pattern(muc_light_helper:muc_host_pattern()),
   85:     [{mod_mam_meta, [{backend, rdbms}, {pm, []}, {muc, [{host, MucHost}]}]},
   86:      {mod_muc_light, [{backend, rdbms}, {host, MucHost}]}];
   87: group_to_modules(muc_light_removal) ->
   88:     MucHost = subhost_pattern(muc_light_helper:muc_host_pattern()),
   89:     [{mod_muc_light, [{backend, rdbms}, {host, MucHost}]}];
   90: group_to_modules(muc_removal) ->
   91:     MucHost = subhost_pattern(muc_helper:muc_host_pattern()),
   92:     [{mod_muc, [{backend, rdbms}, {host, MucHost}]}];
   93: group_to_modules(inbox_removal) ->
   94:     [{mod_inbox, []}];
   95: group_to_modules(private_removal) ->
   96:     [{mod_private, [{backend, rdbms}]}];
   97: group_to_modules(roster_removal) ->
   98:     [{mod_roster, [{backend, rdbms}]}];
   99: group_to_modules(offline_removal) ->
  100:     [{mod_offline, [{backend, rdbms}]}];
  101: group_to_modules(vcard_removal) ->
  102:     [{mod_vcard, [{backend, rdbms}]}];
  103: group_to_modules(last_removal) ->
  104:     [{mod_last, [{backend, rdbms}]}].
  105: 
  106: %%%===================================================================
  107: %%% Testcase specific setup/teardown
  108: %%%===================================================================
  109: 
  110: init_per_testcase(muc_removal, Config) ->
  111:     muc_helper:load_muc(),
  112:     mongoose_helper:ensure_muc_clean(),
  113:     escalus:init_per_testcase(muc_removal, Config);
  114: init_per_testcase(roster_removal, ConfigIn) ->
  115:     Config = roster_helper:set_versioning(true, true, ConfigIn),
  116:     escalus:init_per_testcase(roster_removal, Config);
  117: init_per_testcase(TestCase, Config) ->
  118:     escalus:init_per_testcase(TestCase, Config).
  119: 
  120: end_per_testcase(muc_removal, Config) ->
  121:     mongoose_helper:ensure_muc_clean(),
  122:     muc_helper:unload_muc(),
  123:     escalus:end_per_testcase(muc_removal, Config);
  124: end_per_testcase(TestCase, Config) ->
  125:     escalus:end_per_testcase(TestCase, Config).
  126: 
  127: %%%===================================================================
  128: %%% Test Cases
  129: %%%===================================================================
  130: 
  131: auth_removal(Config) ->
  132:     FreshConfig = escalus_fresh:create_users(Config, [{alice, 1}, {alice_bis, 1}]),
  133:     AliceSpec = escalus_users:get_userspec(FreshConfig, alice),
  134:     AliceBisSpec = escalus_users:get_userspec(FreshConfig, alice_bis),
  135:     connect_and_disconnect(AliceSpec),
  136:     connect_and_disconnect(AliceBisSpec),
  137:     ?assertMatch([_Alice], rpc(mim(), ejabberd_auth, get_vh_registered_users, [domain()])),
  138:     run_remove_domain(),
  139:     ?assertMatch({error, {connection_step_failed, _, _}}, escalus_connection:start(AliceSpec)),
  140:     connect_and_disconnect(AliceBisSpec), % different domain - not removed
  141:     ?assertEqual([], rpc(mim(), ejabberd_auth, get_vh_registered_users, [domain()])).
  142: 
  143: cache_removal(Config) ->
  144:     FreshConfig = escalus_fresh:create_users(Config, [{alice, 1}, {alice_bis, 1}]),
  145:     F = fun(Alice, AliceBis) ->
  146:                 escalus:send(Alice, escalus_stanza:chat_to(AliceBis, <<"Hi!">>)),
  147:                 escalus:wait_for_stanza(AliceBis),
  148:                 mam_helper:wait_for_archive_size(Alice, 1),
  149:                 mam_helper:wait_for_archive_size(AliceBis, 1)
  150:         end,
  151:     escalus:story(FreshConfig, [{alice, 1}, {alice_bis, 1}], F),
  152:     %% Storing the message in MAM should have populated the cache for both users
  153:     ?assertEqual({stop, true}, does_cached_user_exist(FreshConfig, alice)),
  154:     ?assertEqual({stop, true}, does_cached_user_exist(FreshConfig, alice_bis)),
  155:     run_remove_domain(),
  156:     %% Cache removed only for Alice's domain
  157:     ?assertEqual(false, does_cached_user_exist(FreshConfig, alice)),
  158:     ?assertEqual({stop, true}, does_cached_user_exist(FreshConfig, alice_bis)).
  159: 
  160: mam_pm_removal(Config) ->
  161:     F = fun(Alice, Bob) ->
  162:         escalus:send(Alice, escalus_stanza:chat_to(Bob, <<"OH, HAI!">>)),
  163:         escalus:wait_for_stanza(Bob),
  164:         mam_helper:wait_for_archive_size(Alice, 1),
  165:         mam_helper:wait_for_archive_size(Bob, 1),
  166:         run_remove_domain(),
  167:         mam_helper:wait_for_archive_size(Alice, 0),
  168:         mam_helper:wait_for_archive_size(Bob, 0)
  169:         end,
  170:     escalus_fresh:story(Config, [{alice, 1}, {bob, 1}], F).
  171: 
  172: mam_muc_removal(Config0) ->
  173:     F = fun(Config, Alice) ->
  174:         Room = muc_helper:fresh_room_name(),
  175:         MucHost = muc_light_helper:muc_host(),
  176:         muc_light_helper:create_room(Room, MucHost, alice,
  177:                                      [], Config, muc_light_helper:ver(1)),
  178:         RoomAddr = <<Room/binary, "@", MucHost/binary>>,
  179:         escalus:send(Alice, escalus_stanza:groupchat_to(RoomAddr, <<"text">>)),
  180:         escalus:wait_for_stanza(Alice),
  181:         mam_helper:wait_for_room_archive_size(MucHost, Room, 1),
  182:         run_remove_domain(),
  183:         mam_helper:wait_for_room_archive_size(MucHost, Room, 0)
  184:         end,
  185:     escalus_fresh:story_with_config(Config0, [{alice, 1}], F).
  186: 
  187: inbox_removal(Config) ->
  188:     escalus:fresh_story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) ->
  189:         escalus:send(Alice, escalus_stanza:chat_to(Bob, <<"OH, HAI!">>)),
  190:         escalus:wait_for_stanza(Bob),
  191:         inbox_helper:get_inbox(Alice, #{count => 1}),
  192:         inbox_helper:get_inbox(Bob, #{count => 1}),
  193:         run_remove_domain(),
  194:         inbox_helper:get_inbox(Alice, #{count => 0, unread_messages => 0, active_conversations => 0}),
  195:         inbox_helper:get_inbox(Bob, #{count => 0, unread_messages => 0, active_conversations => 0})
  196:       end).
  197: 
  198: muc_removal(Config0) ->
  199:     muc_helper:story_with_room(Config0, [{persistent, true}], [{alice, 1}], fun(Config, Alice) ->
  200:         AliceJid= jid:from_binary(escalus_client:full_jid(Alice)),
  201:         {_, Domain} = jid:to_lus(AliceJid),
  202:         MucHost = muc_helper:muc_host(),
  203:         % Alice joins room and registers nick
  204:         EnterRoom = muc_helper:stanza_muc_enter_room(?config(room, Config), <<"alice">>),
  205:         escalus:send(Alice, EnterRoom),
  206:         escalus:wait_for_stanzas(Alice, 2),
  207:         muc_helper:set_nick(Alice, <<"alice2">>),
  208:         % check muc tables
  209:         ?assertMatch([_], get_muc_rooms(MucHost)),
  210:         ?assertMatch([_], get_muc_room_aff(Domain)),
  211:         ?assertMatch({ok, _}, get_muc_registered(MucHost, AliceJid)),
  212:         % remove domain and check muc tables
  213:         run_remove_domain(),
  214:         ?assertMatch([], get_muc_rooms(MucHost)),
  215:         ?assertMatch([], get_muc_room_aff(Domain)),
  216:         ?assertMatch({error, not_registered}, get_muc_registered(MucHost, AliceJid))
  217:     end).
  218: 
  219: muc_light_removal(Config0) ->
  220:     F = fun(Config, Alice) ->
  221:         %% GIVEN a room
  222:         Room = muc_helper:fresh_room_name(),
  223:         MucHost = muc_light_helper:muc_host(),
  224:         RoomAddr = <<Room/binary, "@", MucHost/binary>>,
  225:         muc_light_helper:create_room(Room, MucHost, alice,
  226:                                      [], Config, muc_light_helper:ver(1)),
  227:         escalus:send(Alice, escalus_stanza:groupchat_to(RoomAddr, <<"text">>)),
  228:         escalus:wait_for_stanza(Alice),
  229:         RoomID = select_room_id(host_type(), Room, MucHost),
  230:         {selected, [_]} = select_affs_by_room_id(host_type(), RoomID),
  231:         {selected, [_|_]} = select_config_by_room_id(host_type(), RoomID),
  232:         {ok, _RoomConfig, _AffUsers, _Version} = get_room_info(host_type(), Room, MucHost),
  233:         %% WHEN domain hook called
  234:         run_remove_domain(),
  235:         %% THEN Room info not available
  236:         {error, not_exists} = get_room_info(host_type(), Room, MucHost),
  237:         %% THEN Tables are empty
  238:         {selected, []} = select_affs_by_room_id(host_type(), RoomID),
  239:         {selected, []} = select_config_by_room_id(host_type(), RoomID)
  240:         end,
  241:     escalus_fresh:story_with_config(Config0, [{alice, 1}], F).
  242: 
  243: muc_light_blocking_removal(Config0) ->
  244:     F = fun(Config, Alice, Bob) ->
  245:         %% GIVEN a room
  246:         Room = muc_helper:fresh_room_name(),
  247:         MucHost = muc_light_helper:muc_host(),
  248:         muc_light_helper:create_room(Room, MucHost, alice,
  249:                                      [], Config, muc_light_helper:ver(1)),
  250:         block_muclight_user(Bob, Alice),
  251:         [_] = get_blocking(host_type(), Bob, MucHost),
  252:         %% WHEN domain hook called
  253:         run_remove_domain(),
  254:         [] = get_blocking(host_type(), Bob, MucHost)
  255:         end,
  256:     escalus_fresh:story_with_config(Config0, [{alice, 1}, {bob, 1}], F).
  257: 
  258: private_removal(Config) ->
  259:     escalus:fresh_story(Config, [{alice, 1}], fun(Alice) ->
  260:         NS = <<"alice:private:ns">>,
  261:         Tag = <<"my_element">>,
  262:         %% Alice stores some data in her private storage
  263:         IqSet = escalus_stanza:private_set(my_banana(NS)),
  264:         IqGet = escalus_stanza:private_get(NS, Tag),
  265:         escalus:send_iq_and_wait_for_result(Alice, IqSet),
  266:         %% Compare results before and after removal
  267:         Res1 = escalus_client:send_iq_and_wait_for_result(Alice, IqGet),
  268:         run_remove_domain(),
  269:         Res2 = escalus_client:send_iq_and_wait_for_result(Alice, IqGet),
  270:         escalus:assert(is_private_result, Res1),
  271:         escalus:assert(is_private_result, Res2),
  272:         Val1 = get_private_data(Res1, Tag, NS),
  273:         Val2 = get_private_data(Res2, Tag, NS),
  274:         ?assert_equal_extra(<<"banana">>, Val1, #{stanza => Res1}),
  275:         ?assert_equal_extra(<<>>, Val2, #{stanza => Res2})
  276:       end).
  277: 
  278: offline_removal(Config) ->
  279:     escalus:fresh_story_with_config(Config, [{alice, 1}, {bob, 1}], fun(FreshConfig, Alice, Bob) ->
  280:         mongoose_helper:logout_user(FreshConfig, Bob),
  281:         escalus:send(Alice, escalus_stanza:chat_to(Bob, <<"msgtxt">>)),
  282:         % wait until message is stored
  283:         BobJid = jid:from_binary(escalus_client:full_jid(Bob)),
  284:         {LUser, LServer} = jid:to_lus(BobJid),
  285:         mongoose_helper:wait_until(
  286:           fun() -> mongoose_helper:total_offline_messages({LUser, LServer}) end, 1),
  287:         % check messages in DB
  288:         ?assertMatch({ok, [_]}, rpc(mim(), mod_offline_rdbms, fetch_messages, [host_type(), BobJid])),
  289:         run_remove_domain(),
  290:         ?assertMatch({ok, []}, rpc(mim(), mod_offline_rdbms, fetch_messages, [host_type(), BobJid]))
  291:     end).
  292: 
  293: roster_removal(Config) ->
  294:     escalus:fresh_story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) ->
  295:         %% add contact
  296:         Stanza = escalus_stanza:roster_add_contact(Bob, [<<"friends">>], <<"Bobby">>),
  297:         escalus:send(Alice, Stanza),
  298:         Received = escalus:wait_for_stanzas(Alice, 2),
  299:         escalus:assert_many([is_roster_set, is_iq_result], Received),
  300: 
  301:         %% check roster
  302:         BobJid = escalus_client:short_jid(Bob),
  303:         Received2 = escalus:send_iq_and_wait_for_result(Alice, escalus_stanza:roster_get()),
  304:         escalus:assert(is_roster_result, Received2),
  305:         escalus:assert(roster_contains, [BobJid], Received2),
  306:         escalus:assert(count_roster_items, [1], Received2),
  307:         ?assertMatch([_], select_from_roster("rosterusers")),
  308:         ?assertMatch([_], select_from_roster("rostergroups")),
  309:         ?assertMatch([_], select_from_roster("roster_version")),
  310: 
  311:         %% remove domain and check roster
  312:         run_remove_domain(),
  313:         Received3 = escalus:send_iq_and_wait_for_result(Alice, escalus_stanza:roster_get()),
  314:         escalus:assert(is_roster_result, Received3),
  315:         escalus:assert(count_roster_items, [0], Received3),
  316:         ?assertMatch([], select_from_roster("rosterusers")),
  317:         ?assertMatch([], select_from_roster("rostergroups")),
  318:         ?assertMatch([], select_from_roster("roster_version"))
  319:     end).
  320: 
  321: vcard_removal(Config) ->
  322:     escalus:fresh_story(Config, [{alice, 1}], fun(Client) ->
  323:         DirJID = <<"vjud.", (domain())/binary>>,
  324:         {LUser, LServer} = jid:to_lus(jid:from_binary(escalus_client:full_jid(Client))),
  325:         VCardFields = [{<<"FN">>, <<"Old name">>}],
  326:         FilterFields = [{<<"fn">>, <<"Old name">>}],
  327:         DbFilterFields = [{<<"fn">>, [<<"Old name">>]}],
  328:         %create vcard for alice
  329:         UpdateResult = escalus:send_and_wait(Client,
  330:                                              escalus_stanza:vcard_update(VCardFields)),
  331:         escalus:assert(is_iq_result, UpdateResult),
  332:         %check before domain removal
  333:         RequestResult = escalus:send_and_wait(Client, escalus_stanza:vcard_request()),
  334:         ?assertMatch(<<"Old name">>, get_vcard_fn(RequestResult)),
  335:         SearchResult = escalus:send_and_wait(Client,
  336:                                              search_vcard_fields(DirJID, FilterFields)),
  337:         ?assertMatch(<<"1">>, get_vcard_search_query_count(SearchResult)),
  338:         ?assertMatch({ok, _}, rpc(mim(), mod_vcard_rdbms, get_vcard,
  339:                                   [host_type(), LUser, LServer])),
  340:         ?assertMatch([_], rpc(mim(), mod_vcard_rdbms, search,
  341:                               [host_type(), LServer, DbFilterFields])),
  342:         %check after domain removal
  343:         run_remove_domain(),
  344:         RequestResult2 = escalus:send_and_wait(Client, escalus_stanza:vcard_request()),
  345:         escalus:assert(is_iq_error, RequestResult2),
  346:         SearchResult2 = escalus:send_and_wait(Client,
  347:                                               search_vcard_fields(DirJID, FilterFields)),
  348:         ?assertMatch(<<"0">>, get_vcard_search_query_count(SearchResult2)),
  349:         ?assertMatch({error, _}, rpc(mim(), mod_vcard_rdbms, get_vcard,
  350:                                      [host_type(), LUser, LServer])),
  351:         ?assertMatch([], rpc(mim(), mod_vcard_rdbms, search,
  352:                              [host_type(), LServer, DbFilterFields]))
  353:     end).
  354: 
  355: last_removal(Config0) ->
  356:     F = fun(Config2, Alice, Bob) ->
  357:             escalus_story:make_all_clients_friends([Alice, Bob]),
  358: 
  359:             %% Bob logs out with a status
  360:             Status = escalus_stanza:tags([{<<"status">>, <<"I am a banana!">>}]),
  361:             Presence = escalus_stanza:presence(<<"unavailable">>, Status),
  362:             escalus_client:send(Bob, Presence),
  363: 
  364:             escalus_client:stop(Config2, Bob),
  365:             timer:sleep(1024), % more than a second
  366: 
  367:             PresUn = escalus_client:wait_for_stanza(Alice),
  368:             escalus:assert(is_presence_with_type, [<<"unavailable">>], PresUn),
  369:     
  370:             %% Alice asks for Bob's last availability
  371:             BobShortJID = escalus_client:short_jid(Bob),
  372:             GetLast = escalus_stanza:last_activity(BobShortJID),
  373:             Stanza = escalus_client:send_iq_and_wait_for_result(Alice, GetLast),
  374:     
  375:             %% Alice receives Bob's status and last online time > 0
  376:             escalus:assert(is_last_result, Stanza),
  377:             true = (1 =< get_last_activity(Stanza)),
  378:             <<"I am a banana!">> = get_last_status(Stanza),
  379:     
  380:             run_remove_domain(),                                         
  381:             escalus_client:send(Alice, GetLast),
  382:             Error = escalus_client:wait_for_stanza(Alice),
  383:             escalus:assert(is_error, [<<"auth">>, <<"forbidden">>], Error)
  384:         end,
  385:     escalus:fresh_story_with_config(Config0, [{alice, 1}, {bob, 1}], F).
  386: 
  387: %% Helpers
  388: 
  389: connect_and_disconnect(Spec) ->
  390:     {ok, Client, _} = escalus_connection:start(Spec),
  391:     escalus_connection:stop(Client).
  392: 
  393: does_cached_user_exist(Config, User) ->
  394:     Jid = #jid{server = Domain} = jid:from_binary(escalus_users:get_jid(Config, User)),
  395:     HostType = domain_to_host_type(mim(), Domain),
  396:     rpc(mim(), mod_cache_users, does_cached_user_exist, [false, HostType, Jid, stored]).
  397: 
  398: search_vcard_fields(DirJID, Filters) ->
  399:     escalus_stanza:search_iq(DirJID, escalus_stanza:search_fields(Filters)).
  400: 
  401: get_vcard_fn(Element) ->
  402:     exml_query:path(Element, [{element, <<"vCard">>},
  403:                               {element, <<"FN">>},
  404:                               cdata]).
  405: 
  406: get_vcard_search_query_count(Element) ->
  407:     exml_query:path(Element, [{element, <<"query">>},
  408:                               {element, <<"set">>},
  409:                               {element, <<"count">>},
  410:                               cdata]).
  411: 
  412: get_muc_registered(MucHost, UserJid) ->
  413:     rpc(mim(), mod_muc_rdbms, get_nick, [host_type(), MucHost, UserJid]).
  414: 
  415: get_muc_rooms(MucHost) ->
  416:     {ok, Rooms} = rpc(mim(), mod_muc_rdbms, get_rooms, [host_type(), MucHost]),
  417:     Rooms.
  418: 
  419: get_muc_room_aff(Domain) ->
  420:     Query = "SELECT * FROM muc_room_aff WHERE lserver = '" ++ binary_to_list(Domain) ++ "'",
  421:     {selected, Affs} = rpc(mim(), mongoose_rdbms, sql_query, [host_type(), Query]),
  422:     Affs.
  423: 
  424: select_from_roster(Table) ->
  425:     Query = "SELECT * FROM " ++ Table ++ " WHERE server='" ++ binary_to_list(domain()) ++ "'",
  426:     {selected, Res} = rpc(mim(), mongoose_rdbms, sql_query, [host_type(), Query]),
  427:     Res.
  428: 
  429: run_remove_domain() ->
  430:     rpc(mim(), mongoose_hooks, remove_domain, [host_type(), domain()]).
  431: 
  432: get_room_info(HostType, RoomU, RoomS) ->
  433:     rpc(mim(), mod_muc_light_db_backend, get_info, [HostType, {RoomU, RoomS}]).
  434: 
  435: select_room_id(MainHost, RoomU, RoomS) ->
  436:     {selected, [{DbRoomID}]} =
  437:         rpc(mim(), mod_muc_light_db_rdbms, select_room_id, [MainHost, RoomU, RoomS]),
  438:     rpc(mim(), mongoose_rdbms, result_to_integer, [DbRoomID]).
  439: 
  440: select_affs_by_room_id(MainHost, RoomID) ->
  441:     rpc(mim(), mod_muc_light_db_rdbms, select_affs_by_room_id, [MainHost, RoomID]).
  442: 
  443: select_config_by_room_id(MainHost, RoomID) ->
  444:     rpc(mim(), mod_muc_light_db_rdbms, select_config_by_room_id, [MainHost, RoomID]).
  445: 
  446: get_blocking(HostType, User, MUCServer) ->
  447:     Jid = jid:from_binary(escalus_client:short_jid(User)),
  448:     {LUser, LServer, _} = jid:to_lower(Jid),
  449:     rpc(mim(), mod_muc_light_db_rdbms, get_blocking, [HostType, {LUser, LServer}, MUCServer]).
  450: 
  451: block_muclight_user(Bob, Alice) ->
  452:     %% Bob blocks Alice
  453:     AliceJIDBin = escalus_client:short_jid(Alice),
  454:     BlocklistChange = [{user, deny, AliceJIDBin}],
  455:     escalus:send(Bob, muc_light_helper:stanza_blocking_set(BlocklistChange)),
  456:     escalus:assert(is_iq_result, escalus:wait_for_stanza(Bob)).
  457: 
  458: my_banana(NS) ->
  459:     #xmlel{
  460:         name = <<"my_element">>,
  461:         attrs = [{<<"xmlns">>, NS}],
  462:         children = [#xmlcdata{content = <<"banana">>}]}.
  463: 
  464: get_private_data(Elem, Tag, NS) ->
  465:     Path = [{element, <<"query">>}, {element_with_ns, Tag, NS}, cdata],
  466:     exml_query:path(Elem, Path).
  467: 
  468: get_last_activity(Stanza) ->
  469:     S = exml_query:path(Stanza, [{element, <<"query">>}, {attr, <<"seconds">>}]),
  470:     list_to_integer(binary_to_list(S)).
  471: 
  472: get_last_status(Stanza) ->
  473:     exml_query:path(Stanza, [{element, <<"query">>}, cdata]).