1: -module(muc_light_SUITE). 2: 3: -include_lib("escalus/include/escalus.hrl"). 4: -include_lib("escalus/include/escalus_xmlns.hrl"). 5: -include_lib("exml/include/exml.hrl"). 6: -include_lib("eunit/include/eunit.hrl"). 7: -include("mam_helper.hrl"). 8: 9: -export([ % service 10: removing_users_from_server_triggers_room_destruction/1 11: ]). 12: -export([ % entity 13: disco_service/1, 14: disco_features/1, 15: disco_features_with_mam/1, 16: disco_info/1, 17: disco_info_with_mam/1, 18: disco_rooms/1, 19: disco_rooms_empty_page_1/1, 20: disco_rooms_empty_page_infinity/1, 21: disco_rooms_created_page_1/1, 22: disco_rooms_created_page_infinity/1, 23: disco_rooms_rsm/1, 24: rooms_in_rosters/1, 25: rooms_in_rosters_doesnt_break_disco_info/1, 26: no_roomname_in_schema_doesnt_break_disco_and_roster/1, 27: unauthorized_stanza/1 28: ]). 29: -export([ % occupant 30: send_message/1, 31: change_subject/1, 32: change_roomname/1, 33: all_can_configure/1, 34: set_config_deny/1, 35: get_room_config/1, 36: custom_default_config_works/1, 37: get_room_occupants/1, 38: get_room_info/1, 39: leave_room/1, 40: change_other_aff_deny/1 41: ]). 42: -export([ % owner 43: create_room/1, 44: create_room_unique/1, 45: create_room_with_equal_occupants/1, 46: create_existing_room_deny/1, 47: destroy_room/1, 48: destroy_room_get_disco_items_empty/1, 49: destroy_room_get_disco_items_one_left/1, 50: set_config/1, 51: set_config_with_custom_schema/1, 52: deny_config_change_that_conflicts_with_schema/1, 53: assorted_config_doesnt_lead_to_duplication/1, 54: remove_and_add_users/1, 55: explicit_owner_change/1, 56: implicit_owner_change/1, 57: edge_case_owner_change/1, 58: adding_wrongly_named_user_triggers_infinite_loop/1 59: ]). 60: -export([ % limits 61: rooms_per_user/1, 62: max_occupants/1 63: ]). 64: -export([ % blocking 65: manage_blocklist/1, 66: block_room/1, 67: block_user/1, 68: blocking_disabled/1 69: ]). 70: 71: -export([all/0, groups/0, suite/0, 72: init_per_suite/1, end_per_suite/1, 73: init_per_group/2, end_per_group/2, 74: init_per_testcase/2, end_per_testcase/2]). 75: 76: -import(escalus_ejabberd, [rpc/3]). 77: -import(muc_helper, [foreach_occupant/3, foreach_recipient/2]). 78: -import(distributed_helper, [subhost_pattern/1]). 79: -import(domain_helper, [host_type/0, domain/0]). 80: -import(muc_light_helper, [ 81: bin_aff_users/1, 82: gc_message_verify_fun/3, 83: lbin/1, 84: room_bin_jid/1, 85: verify_aff_bcast/2, 86: verify_aff_bcast/3, 87: verify_aff_users/2, 88: kv_el/2, 89: stanza_create_room/3, 90: create_room/6, 91: stanza_aff_set/2, 92: user_leave/3, 93: stanza_blocking_set/1, 94: default_config/1, 95: default_schema/0 96: ]). 97: -import(config_parser_helper, [mod_config/2]). 98: 99: -include("muc_light.hrl"). 100: 101: -define(ROOM, <<"testroom">>). 102: -define(ROOM2, <<"testroom2">>). 103: 104: -define(MUCHOST, (muc_light_helper:muc_host())). 105: 106: -type ct_aff_user() :: {EscalusClient :: escalus:client(), Aff :: atom()}. 107: -type ct_aff_users() :: [ct_aff_user()]. 108: -type ct_block_item() :: muc_light_helper:ct_block_item(). 109: -type verify_fun() :: muc_helper:verify_fun(). 110: -type xmlel() :: exml:element(). 111: 112: -define(DEFAULT_AFF_USERS, [{Alice, owner}, {Bob, member}, {Kate, member}]). 113: 114: %%-------------------------------------------------------------------- 115: %% Suite configuration 116: %%-------------------------------------------------------------------- 117: 118: all() -> 119: [ 120: {group, service}, 121: {group, entity}, 122: {group, occupant}, 123: {group, owner}, 124: {group, blocking}, 125: {group, limits} 126: ]. 127: 128: groups() -> 129: G = [ 130: {service, [sequence], [ 131: removing_users_from_server_triggers_room_destruction 132: ]}, 133: {entity, [sequence], [ 134: disco_service, 135: disco_features, 136: disco_features_with_mam, 137: disco_info, 138: disco_info_with_mam, 139: disco_rooms, 140: disco_rooms_rsm, 141: disco_rooms_created_page_1, 142: disco_rooms_created_page_infinity, 143: disco_rooms_empty_page_infinity, 144: disco_rooms_empty_page_1, 145: unauthorized_stanza, 146: rooms_in_rosters, 147: rooms_in_rosters_doesnt_break_disco_info, 148: no_roomname_in_schema_doesnt_break_disco_and_roster 149: ]}, 150: {occupant, [sequence], [ 151: send_message, 152: change_subject, 153: change_roomname, 154: all_can_configure, 155: set_config_deny, 156: get_room_config, 157: custom_default_config_works, 158: get_room_occupants, 159: get_room_info, 160: leave_room, 161: change_other_aff_deny 162: ]}, 163: {owner, [sequence], [ 164: create_room, 165: create_room_unique, 166: create_room_with_equal_occupants, 167: create_existing_room_deny, 168: destroy_room, 169: destroy_room_get_disco_items_empty, 170: destroy_room_get_disco_items_one_left, 171: set_config, 172: set_config_with_custom_schema, 173: deny_config_change_that_conflicts_with_schema, 174: assorted_config_doesnt_lead_to_duplication, 175: remove_and_add_users, 176: explicit_owner_change, 177: implicit_owner_change, 178: edge_case_owner_change, 179: adding_wrongly_named_user_triggers_infinite_loop 180: ]}, 181: {limits, [sequence], [ 182: rooms_per_user, 183: max_occupants 184: ]}, 185: {blocking, [sequence], [ 186: manage_blocklist, 187: block_room, 188: block_user, 189: blocking_disabled 190: ]} 191: ], 192: ct_helper:repeat_all_until_all_ok(G). 193: 194: suite() -> 195: escalus:suite(). 196: 197: %%-------------------------------------------------------------------- 198: %% Init & teardown 199: %%-------------------------------------------------------------------- 200: 201: init_per_suite(Config) -> 202: Config1 = dynamic_modules:save_modules(host_type(), Config), 203: Config2 = escalus:init_per_suite(Config1), 204: escalus:create_users(Config2, escalus:get_users([alice, bob, kate, mike])). 205: 206: end_per_suite(Config) -> 207: HostType = host_type(), 208: muc_light_helper:clear_db(HostType), 209: Config1 = escalus:delete_users(Config, escalus:get_users([alice, bob, kate, mike])), 210: dynamic_modules:restore_modules(Config), 211: escalus:end_per_suite(Config1). 212: 213: init_per_group(_GroupName, Config) -> 214: Config. 215: 216: end_per_group(_GroupName, Config) -> 217: Config. 218: 219: -define(ROOM_LESS_CASES, [disco_rooms_empty_page_infinity, 220: disco_rooms_empty_page_1]). 221: 222: -define(CUSTOM_CONFIG_CASES, [set_config_with_custom_schema, 223: deny_config_change_that_conflicts_with_schema, 224: no_roomname_in_schema_doesnt_break_disco_and_roster, 225: custom_default_config_works]). 226: 227: init_per_testcase(removing_users_from_server_triggers_room_destruction = CaseName, Config) -> 228: dynamic_modules:ensure_modules(host_type(), required_modules(CaseName)), 229: Config1 = escalus:create_users(Config, escalus:get_users([carol])), 230: create_room(?ROOM, ?MUCHOST, carol, [], Config1, ver(1)), 231: escalus:init_per_testcase(CaseName, Config1); 232: init_per_testcase(disco_rooms_rsm = CaseName, Config) -> 233: dynamic_modules:ensure_modules(host_type(), required_modules(CaseName)), 234: create_room(?ROOM, ?MUCHOST, alice, [bob, kate], Config, ver(1)), 235: create_room(?ROOM2, ?MUCHOST, alice, [bob, kate], Config, ver(1)), 236: escalus:init_per_testcase(disco_rooms_rsm, Config); 237: init_per_testcase(CaseName, Config) when CaseName =:= disco_features_with_mam; 238: CaseName =:= disco_info_with_mam -> 239: case mam_helper:backend() of 240: disabled -> 241: {skip, "No MAM backend available"}; 242: Backend -> 243: dynamic_modules:ensure_modules(host_type(), required_modules(CaseName, Backend)), 244: create_room(?ROOM, ?MUCHOST, alice, [bob, kate], Config, ver(1)), 245: escalus:init_per_testcase(CaseName, Config) 246: end; 247: init_per_testcase(CaseName, Config) -> 248: dynamic_modules:ensure_modules(host_type(), required_modules(CaseName)), 249: case lists:member(CaseName, ?ROOM_LESS_CASES) of 250: false -> create_room(?ROOM, ?MUCHOST, alice, [bob, kate], Config, ver(1)); 251: _ -> ok 252: end, 253: escalus:init_per_testcase(CaseName, Config). 254: 255: end_per_testcase(CaseName, Config) -> 256: muc_light_helper:clear_db(host_type()), 257: escalus:end_per_testcase(CaseName, Config). 258: 259: %% Module configuration per test case 260: 261: required_modules(CaseName, MAMBackend) -> 262: [{mod_mam, mam_helper:config_opts(#{backend => MAMBackend, 263: muc => #{host => subhost_pattern(?MUCHOST)}})} | 264: common_required_modules(CaseName)]. 265: 266: required_modules(CaseName) -> 267: [{mod_mam, stopped} | common_required_modules(CaseName)]. 268: 269: common_required_modules(CaseName) -> 270: BasicOpts = maps:merge(common_muc_light_opts(), muc_light_opts(CaseName)), 271: Opts = maps:merge(BasicOpts, schema_opts(CaseName)), 272: [{mod_muc_light, mod_config(mod_muc_light, Opts)}]. 273: 274: muc_light_opts(CaseName) when CaseName =:= disco_rooms_rsm; 275: CaseName =:= disco_rooms_empty_page_1; 276: CaseName =:= rooms_created_page_1 -> 277: #{rooms_per_page => 1}; 278: muc_light_opts(CaseName) when CaseName =:= disco_rooms_empty_page_infinity; 279: CaseName =:= disco_rooms_created_page_infinity -> 280: #{rooms_per_page => infinity}; 281: muc_light_opts(all_can_configure) -> 282: #{all_can_configure => true}; 283: muc_light_opts(create_room_with_equal_occupants) -> 284: #{equal_occupants => true}; 285: muc_light_opts(rooms_per_user) -> 286: #{rooms_per_user => 1}; 287: muc_light_opts(max_occupants) -> 288: #{max_occupants => 1}; 289: muc_light_opts(block_user) -> 290: #{all_can_invite => true}; 291: muc_light_opts(blocking_disabled) -> 292: #{blocking => false}; 293: muc_light_opts(_) -> 294: #{}. 295: 296: schema_opts(CaseName) -> 297: case lists:member(CaseName, ?CUSTOM_CONFIG_CASES) of 298: true -> #{config_schema => custom_schema()}; 299: false -> #{} 300: end. 301: 302: common_muc_light_opts() -> 303: #{backend => mongoose_helper:mnesia_or_rdbms_backend(), 304: cache_affs => config_parser_helper:default_config([modules, mod_muc_light, cache_affs]), 305: rooms_in_rosters => true}. 306: 307: %%-------------------------------------------------------------------- 308: %% MUC light tests 309: %%-------------------------------------------------------------------- 310: 311: %% ---------------------- Service ---------------------- 312: 313: removing_users_from_server_triggers_room_destruction(Config) -> 314: escalus:delete_users(Config, escalus:get_users([carol])), 315: {error, not_exists} = rpc(mod_muc_light_db_backend, get_info, [host_type(), {?ROOM, ?MUCHOST}]). 316: 317: %% ---------------------- Disco ---------------------- 318: 319: disco_service(Config) -> 320: escalus:story(Config, [{alice, 1}], fun(Alice) -> 321: Server = escalus_client:server(Alice), 322: escalus:send(Alice, escalus_stanza:service_discovery(Server)), 323: Stanza = escalus:wait_for_stanza(Alice), 324: Query = exml_query:subelement(Stanza, <<"query">>), 325: Item = exml_query:subelement_with_attr(Query, <<"jid">>, ?MUCHOST), 326: ?assertEqual(?NS_MUC_LIGHT, exml_query:attr(Item, <<"node">>)), 327: escalus:assert(is_stanza_from, [domain()], Stanza) 328: end). 329: 330: disco_features(Config) -> 331: disco_features_story(Config, false). 332: 333: disco_features_with_mam(Config) -> 334: disco_features_story(Config, true). 335: 336: disco_features_story(Config, HasMAM) -> 337: escalus:story(Config, [{alice, 1}], fun(Alice) -> 338: DiscoStanza = escalus_stanza:to(escalus_stanza:iq_get(?NS_DISCO_INFO, []), ?MUCHOST), 339: escalus:send(Alice, DiscoStanza), 340: Stanza = escalus:wait_for_stanza(Alice), 341: <<"conference">> = exml_query:path(Stanza, [{element, <<"query">>}, 342: {element, <<"identity">>}, 343: {attr, <<"category">>}]), 344: check_features(Stanza, HasMAM), 345: escalus:assert(is_stanza_from, [?MUCHOST], Stanza) 346: end). 347: 348: disco_info(Config) -> 349: disco_info_story(Config, false). 350: 351: disco_info_with_mam(Config) -> 352: disco_info_story(Config, true). 353: 354: disco_info_story(Config, HasMAM) -> 355: escalus:story(Config, [{alice, 1}], fun(Alice) -> 356: RoomJID = room_bin_jid(?ROOM), 357: DiscoStanza = escalus_stanza:to(escalus_stanza:iq_get(?NS_DISCO_INFO, []), RoomJID), 358: escalus:send(Alice, DiscoStanza), 359: Stanza = escalus:wait_for_stanza(Alice), 360: check_features(Stanza, HasMAM), 361: escalus:assert(is_stanza_from, [RoomJID], Stanza) 362: end). 363: 364: check_features(Stanza, HasMAM) -> 365: ExpectedFeatures = expected_features(HasMAM), 366: ActualFeatures = exml_query:paths(Stanza, [{element, <<"query">>}, 367: {element, <<"feature">>}, 368: {attr, <<"var">>}]), 369: ?assertEqual(ExpectedFeatures, ActualFeatures). 370: 371: expected_features(true) -> 372: lists:sort([?NS_MUC_LIGHT | mam_helper:namespaces()]); 373: expected_features(false) -> 374: [?NS_MUC_LIGHT]. 375: 376: %% The room list is empty. Rooms_per_page set to `infinity` 377: disco_rooms_empty_page_infinity(Config) -> 378: escalus:story(Config, [{alice, 1}], fun(Alice) -> 379: [] = get_disco_rooms(Alice) 380: end). 381: 382: %% The room list is empty. Rooms_per_page set to 1 383: disco_rooms_empty_page_1(Config) -> 384: escalus:story(Config, [{alice, 1}], fun(Alice) -> 385: [] = get_disco_rooms(Alice) 386: end). 387: 388: %% There is one room created. Rooms_per_page set to 1 389: disco_rooms_created_page_1(Config) -> 390: escalus:story(Config, [{alice, 1}], fun verify_user_has_one_room/1). 391: 392: %% There is one room created. Rooms_per_page set to `infinity` 393: disco_rooms_created_page_infinity(Config) -> 394: escalus:story(Config, [{alice, 1}], fun verify_user_has_one_room/1). 395: 396: disco_rooms(Config) -> 397: escalus:story(Config, [{alice, 1}], fun(Alice) -> 398: MucHost = ?MUCHOST, 399: {ok, {?ROOM2, MucHost}} = create_room(?ROOM2, ?MUCHOST, kate, [], Config, ver(0)), 400: %% we should get 1 room, Alice is not in the second one 401: [Item] = get_disco_rooms(Alice), 402: ProperJID = room_bin_jid(?ROOM), 403: ProperJID = exml_query:attr(Item, <<"jid">>), 404: ProperVer = ver(1), 405: ProperVer = exml_query:attr(Item, <<"version">>) 406: end). 407: 408: disco_rooms_rsm(Config) -> 409: escalus:story(Config, [{alice, 1}], fun(Alice) -> 410: DiscoStanza = escalus_stanza:to(escalus_stanza:iq_get(?NS_DISCO_ITEMS, []), ?MUCHOST), 411: escalus:send(Alice, DiscoStanza), 412: %% we should get 1 room with RSM info 413: Stanza = escalus:wait_for_stanza(Alice), 414: [Item] = exml_query:paths(Stanza, [{element, <<"query">>}, {element, <<"item">>}]), 415: ProperJID = room_bin_jid(?ROOM), 416: ProperJID = exml_query:attr(Item, <<"jid">>), 417: 418: RSM = #xmlel{ name = <<"set">>, 419: attrs = [{<<"xmlns">>, ?NS_RSM}], 420: children = [ #xmlel{ name = <<"max">>, 421: children = [#xmlcdata{ content = <<"10">> }] }, 422: #xmlel{ name = <<"before">> } ] }, 423: DiscoStanza2 = escalus_stanza:to( 424: escalus_stanza:iq_get(?NS_DISCO_ITEMS, [RSM]), ?MUCHOST), 425: escalus:send(Alice, DiscoStanza2), 426: %% we should get second room 427: Stanza2 = escalus:wait_for_stanza(Alice), 428: [Item2] = exml_query:paths(Stanza2, [{element, <<"query">>}, {element, <<"item">>}]), 429: ProperJID2 = room_bin_jid(?ROOM2), 430: ProperJID2 = exml_query:attr(Item2, <<"jid">>), 431: 432: BadAfter = #xmlel{ name = <<"after">>, 433: children = [#xmlcdata{ content = <<"oops@muclight.localhost">> }] }, 434: RSM2 = #xmlel{ name = <<"set">>, 435: attrs = [{<<"xmlns">>, ?NS_RSM}], 436: children = [ #xmlel{ name = <<"max">>, 437: children = [#xmlcdata{ content = <<"10">> }] }, 438: BadAfter ] }, 439: DiscoStanza3 = escalus_stanza:to( 440: escalus_stanza:iq_get(?NS_DISCO_ITEMS, [RSM2]), ?MUCHOST), 441: escalus:send(Alice, DiscoStanza3), 442: escalus:assert(is_error, [<<"cancel">>, <<"item-not-found">>], 443: escalus:wait_for_stanza(Alice)) 444: end). 445: 446: rooms_in_rosters(Config) -> 447: escalus:story(Config, [{alice, 1}], fun(Alice) -> 448: AliceU = escalus_utils:jid_to_lower(escalus_client:username(Alice)), 449: AliceS = escalus_utils:jid_to_lower(escalus_client:server(Alice)), 450: escalus:send(Alice, escalus_stanza:roster_get()), 451: mongoose_helper:wait_until( 452: fun() -> 453: distributed_helper:rpc( 454: distributed_helper:mim(), 455: mod_roster, 456: get_user_rosters_length, 457: [host_type(), jid:make(AliceU, AliceS, <<>>)]) 458: end, 1, #{time_left => timer:seconds(10)}), 459: RosterResult = escalus:wait_for_stanza(Alice), 460: escalus_assert:is_roster_result(RosterResult), 461: 462: [Item] = exml_query:paths( 463: RosterResult, [{element, <<"query">>}, {element, <<"item">>}]), 464: ProperJID = room_bin_jid(?ROOM), 465: ProperJID = exml_query:attr(Item, <<"jid">>), 466: ProperName = proplists:get_value(<<"roomname">>, default_config(default_schema())), 467: ProperName = exml_query:attr(Item, <<"name">>), 468: ProperVer = ver(1), 469: ProperVer = exml_query:path(Item, [{element, <<"version">>}, cdata]) 470: end). 471: 472: rooms_in_rosters_doesnt_break_disco_info(Config) -> 473: escalus:story(Config, [{alice, 1}], fun(Alice) -> 474: % Verify that room is in roster 475: escalus:send(Alice, escalus_stanza:roster_get()), 476: RosterResult = escalus:wait_for_stanza(Alice), 477: [RosterItem] = exml_query:paths( 478: RosterResult, [{element, <<"query">>}, {element, <<"item">>}]), 479: RoomJID = room_bin_jid(?ROOM), 480: RoomJID = exml_query:attr(RosterItem, <<"jid">>), 481: 482: % Verify that disco#info doesn't crash when rooms are in roster 483: DiscoStanza = escalus_stanza:disco_info(escalus_client:short_jid(Alice)), 484: IQRes = escalus:send_iq_and_wait_for_result(Alice, DiscoStanza), 485: escalus:assert(is_iq_result, IQRes) 486: end). 487: 488: no_roomname_in_schema_doesnt_break_disco_and_roster(Config) -> 489: escalus:story(Config, [{alice, 1}], fun(Alice) -> 490: [DiscoItem] = get_disco_rooms(Alice), 491: ?assert_equal_extra(?ROOM, exml_query:attr(DiscoItem, <<"name">>), 492: #{elem => DiscoItem}), 493: 494: escalus:send(Alice, escalus_stanza:roster_get()), 495: RosterResult = escalus:wait_for_stanza(Alice), 496: [RosterItem] = exml_query:paths( 497: RosterResult, [{element, <<"query">>}, {element, <<"item">>}]), 498: ?ROOM = exml_query:attr(RosterItem, <<"name">>) 499: end). 500: 501: unauthorized_stanza(Config) -> 502: escalus:story(Config, [{alice, 1}, {kate, 1}], fun(Alice, Kate) -> 503: MucHost = ?MUCHOST, 504: {ok, {?ROOM2, MucHost}} = create_room(?ROOM2, ?MUCHOST, kate, [], Config, ver(0)), 505: MsgStanza = escalus_stanza:groupchat_to(room_bin_jid(?ROOM2), <<"malicious">>), 506: escalus:send(Alice, MsgStanza), 507: escalus:assert(is_error, [<<"cancel">>, <<"item-not-found">>], 508: escalus:wait_for_stanza(Alice)), 509: verify_no_stanzas([Alice, Kate]) 510: end). 511: 512: %% ---------------------- Occupant ---------------------- 513: 514: send_message(Config) -> 515: escalus:story(Config, [{alice, 1}, {bob, 1}, {kate, 1}], fun(Alice, Bob, Kate) -> 516: Msg = <<"Heyah!">>, 517: Id = <<"MyID">>, 518: Stanza = escalus_stanza:set_id( 519: escalus_stanza:groupchat_to(room_bin_jid(?ROOM), Msg), Id), 520: foreach_occupant([Alice, Bob, Kate], Stanza, gc_message_verify_fun(?ROOM, Msg, Id)) 521: end). 522: 523: change_subject(Config) -> 524: escalus:story(Config, [{alice, 1}, {bob, 1}, {kate, 1}], fun(Alice, Bob, Kate) -> 525: ConfigChange = [{<<"subject">>, <<"new subject">>}], 526: Stanza = stanza_config_set(?ROOM, ConfigChange), 527: foreach_occupant([Alice, Bob, Kate], Stanza, config_msg_verify_fun(ConfigChange)) 528: end). 529: 530: change_roomname(Config) -> 531: escalus:story(Config, [{alice, 1}], fun(Alice) -> 532: %% change room name 533: ConfigChange = [{<<"roomname">>, <<"new_test_room">>}], 534: Stanza = stanza_config_set(?ROOM, ConfigChange), 535: escalus:send(Alice, Stanza), 536: escalus:wait_for_stanzas(Alice, 2), 537: StanzaCheck = stanza_config_get(?ROOM, ver(1)), 538: escalus:send(Alice, StanzaCheck), 539: Res = escalus:wait_for_stanza(Alice), 540: [_] = exml_query:paths(Res, [{element, <<"query">>}, {element, <<"roomname">>}]) 541: end). 542: 543: all_can_configure(Config) -> 544: escalus:story(Config, [{alice, 1}, {bob, 1}, {kate, 1}], fun(Alice, Bob, Kate) -> 545: ConfigChange = [{<<"roomname">>, <<"new subject">>}], 546: Stanza = stanza_config_set(?ROOM, ConfigChange), 547: foreach_occupant([Alice, Bob, Kate], Stanza, config_msg_verify_fun(ConfigChange)) 548: end). 549: 550: set_config_deny(Config) -> 551: escalus:story(Config, [{alice, 1}, {bob, 1}, {kate, 1}], fun(Alice, Bob, Kate) -> 552: ConfigChange = [{<<"roomname">>, <<"new subject">>}], 553: Stanza = stanza_config_set(?ROOM, ConfigChange), 554: escalus:send(Kate, Stanza), 555: escalus:assert(is_error, [<<"cancel">>, <<"not-allowed">>], 556: escalus:wait_for_stanza(Kate)), 557: verify_no_stanzas([Alice, Bob, Kate]) 558: end). 559: 560: get_room_config(Config) -> 561: escalus:story(Config, [{alice, 1}, {bob, 1}, {kate, 1}], fun(Alice, Bob, Kate) -> 562: Stanza = stanza_config_get(?ROOM, <<"oldver">>), 563: ConfigKV = [{<<"version">>, ver(1)} | default_config(default_schema())], 564: foreach_occupant([Alice, Bob, Kate], Stanza, config_iq_verify_fun(ConfigKV)), 565: 566: %% Empty result when user has most recent version 567: escalus:send(Bob, stanza_config_get(?ROOM, ver(1))), 568: IQRes = escalus:wait_for_stanza(Bob), 569: escalus:assert(is_iq_result, IQRes), 570: undefined = exml_query:subelement(IQRes, <<"query">>) 571: end). 572: 573: custom_default_config_works(Config) -> 574: escalus:story(Config, [{alice, 1}, {bob, 1}, {kate, 1}], fun(Alice, Bob, Kate) -> 575: Stanza = stanza_config_get(?ROOM, <<"oldver">>), 576: ConfigKV = [{<<"version">>, ver(1)} | default_config(custom_schema())], 577: foreach_occupant([Alice, Bob, Kate], Stanza, config_iq_verify_fun(ConfigKV)) 578: end). 579: 580: 581: get_room_occupants(Config) -> 582: escalus:story(Config, [{alice, 1}, {bob, 1}, {kate, 1}], fun(Alice, Bob, Kate) -> 583: AffUsers = [{Alice, owner}, {Bob, member}, {Kate, member}], 584: foreach_occupant([Alice, Bob, Kate], stanza_aff_get(?ROOM, <<"oldver">>), 585: aff_iq_verify_fun(AffUsers, ver(1))), 586: 587: escalus:send(Bob, stanza_aff_get(?ROOM, ver(1))), 588: IQRes = escalus:wait_for_stanza(Bob), 589: escalus:assert(is_iq_result, IQRes), 590: undefined = exml_query:subelement(IQRes, <<"query">>) 591: end). 592: 593: get_room_info(Config) -> 594: escalus:story(Config, [{alice, 1}, {bob, 1}, {kate, 1}], fun(Alice, Bob, Kate) -> 595: Stanza = stanza_info_get(?ROOM, <<"oldver">>), 596: ConfigKV = default_config(default_schema()), 597: foreach_occupant([Alice, Bob, Kate], Stanza, 598: info_iq_verify_fun(?DEFAULT_AFF_USERS, ver(1), ConfigKV)), 599: 600: escalus:send(Bob, stanza_aff_get(?ROOM, ver(1))), 601: IQRes = escalus:wait_for_stanza(Bob), 602: escalus:assert(is_iq_result, IQRes), 603: undefined = exml_query:subelement(IQRes, <<"query">>) 604: end). 605: 606: leave_room(Config) -> 607: escalus:story(Config, [{alice, 1}, {bob, 1}, {kate, 1}], fun(Alice, Bob, Kate) -> 608: % Users will leave one by one, owner last 609: lists:foldr( 610: fun(User, {Occupants, Outsiders}) -> 611: NewOccupants = lists:keydelete(User, 1, Occupants), 612: user_leave(?ROOM, User, NewOccupants), 613: verify_no_stanzas(Outsiders), 614: {NewOccupants, [User | Outsiders]} 615: end, {?DEFAULT_AFF_USERS, []}, [Alice, Bob, Kate]), 616: 617: % Now we verify that room is removed from DB 618: {error, not_exists} = rpc(mod_muc_light_db_backend, get_info, [host_type(), {?ROOM, ?MUCHOST}]) 619: end). 620: 621: change_other_aff_deny(Config) -> 622: escalus:story(Config, [{alice, 1}, {bob, 1}, {kate, 1}, {mike, 1}], 623: fun(Alice, Bob, Kate, Mike) -> 624: AffUsersChanges1 = [{Bob, none}], 625: escalus:send(Kate, stanza_aff_set(?ROOM, AffUsersChanges1)), 626: escalus:assert(is_error, [<<"cancel">>, <<"not-allowed">>], 627: escalus:wait_for_stanza(Kate)), 628: 629: AffUsersChanges2 = [{Alice, member}, {Kate, owner}], 630: escalus:send(Kate, stanza_aff_set(?ROOM, AffUsersChanges2)), 631: escalus:assert(is_error, [<<"cancel">>, <<"not-allowed">>], 632: escalus:wait_for_stanza(Kate)), 633: 634: AffUsersChanges3 = [{Mike, member}], 635: escalus:send(Kate, stanza_aff_set(?ROOM, AffUsersChanges3)), 636: escalus:assert(is_error, [<<"cancel">>, <<"not-allowed">>], 637: escalus:wait_for_stanza(Kate)), 638: 639: verify_no_stanzas([Alice, Bob, Kate, Mike]) 640: end). 641: 642: %% ---------------------- owner ---------------------- 643: 644: create_room(Config) -> 645: escalus:story(Config, [{alice, 1}, {bob, 1}, {kate, 1}], fun(Alice, Bob, Kate) -> 646: InitOccupants = [{Alice, member}, 647: {Kate, member}], 648: FinalOccupants = [{Bob, owner} | InitOccupants], 649: InitConfig = [{<<"roomname">>, <<"Bob's room">>}], 650: RoomNode = <<"bobroom">>, 651: escalus:send(Bob, stanza_create_room(RoomNode, InitConfig, InitOccupants)), 652: verify_aff_bcast(FinalOccupants, FinalOccupants), 653: escalus:assert(is_iq_result, escalus:wait_for_stanza(Bob)) 654: end). 655: 656: create_room_unique(Config) -> 657: escalus:story(Config, [{bob, 1}], fun(Bob) -> 658: InitConfig = [{<<"roomname">>, <<"Bob's room">>}], 659: escalus:send(Bob, stanza_create_room(undefined, InitConfig, [])), 660: verify_aff_bcast([{Bob, owner}], [{Bob, owner}]), 661: escalus:assert(is_iq_result, escalus:wait_for_stanza(Bob)) 662: end). 663: 664: create_room_with_equal_occupants(Config) -> 665: escalus:story(Config, [{bob, 1}], fun(Bob) -> 666: InitConfig = [{<<"roomname">>, <<"Bob's room">>}], 667: escalus:send(Bob, stanza_create_room(undefined, InitConfig, [])), 668: verify_aff_bcast([{Bob, member}], [{Bob, member}]), 669: escalus:assert(is_iq_result, escalus:wait_for_stanza(Bob)) 670: end). 671: 672: create_existing_room_deny(Config) -> 673: escalus:story(Config, [{bob, 1}], fun(Bob) -> 674: escalus:send(Bob, stanza_create_room(?ROOM, [], [])), 675: escalus:assert(is_error, [<<"cancel">>, <<"conflict">>], escalus:wait_for_stanza(Bob)) 676: end). 677: 678: destroy_room(Config) -> 679: escalus:story(Config, [{alice, 1}, {bob, 1}, {kate, 1}], fun(Alice, Bob, Kate) -> 680: escalus:send(Alice, stanza_destroy_room(?ROOM)), 681: AffUsersChanges = [{Bob, none}, {Alice, none}, {Kate, none}], 682: verify_aff_bcast([], AffUsersChanges, [?NS_MUC_LIGHT_DESTROY]), 683: escalus:assert(is_iq_result, escalus:wait_for_stanza(Alice)) 684: end). 685: 686: destroy_room_get_disco_items_empty(Config) -> 687: escalus:story(Config, [{alice, 1}, {bob, 1}, {kate, 1}], fun(Alice, Bob, Kate) -> 688: escalus:send(Alice, stanza_destroy_room(?ROOM)), 689: AffUsersChanges = [{Bob, none}, {Alice, none}, {Kate, none}], 690: verify_aff_bcast([], AffUsersChanges, [?NS_MUC_LIGHT_DESTROY]), 691: escalus:assert(is_iq_result, escalus:wait_for_stanza(Alice)), 692: % Send disco#items request 693: DiscoStanza = escalus_stanza:to(escalus_stanza:iq_get(?NS_DISCO_ITEMS, []), ?MUCHOST), 694: foreach_occupant([Alice, Bob, Kate], DiscoStanza, disco_items_verify_fun([])) 695: end). 696: 697: destroy_room_get_disco_items_one_left(Config) -> 698: escalus:story(Config, [{alice, 1}, {bob, 1}, {kate, 1}], fun(Alice, Bob, Kate) -> 699: MucHost = ?MUCHOST, 700: {ok, {?ROOM2, MucHost}} = create_room(?ROOM2, ?MUCHOST, kate, 701: [bob, alice], Config, ver(0)), 702: ProperJID = room_bin_jid(?ROOM2), 703: %% alie destroy her room 704: escalus:send(Alice, stanza_destroy_room(?ROOM)), 705: AffUsersChanges = [{Bob, none}, {Alice, none}, {Kate, none}], 706: verify_aff_bcast([], AffUsersChanges, [?NS_MUC_LIGHT_DESTROY]), 707: escalus:assert(is_iq_result, escalus:wait_for_stanza(Alice)), 708: % Send disco#items request. Shoul be one room created by kate 709: DiscoStanza = escalus_stanza:to(escalus_stanza:iq_get(?NS_DISCO_ITEMS, []), ?MUCHOST), 710: foreach_occupant([Alice, Bob, Kate], DiscoStanza, disco_items_verify_fun([ProperJID])) 711: end). 712: 713: set_config(Config) -> 714: escalus:story(Config, [{alice, 1}, {bob, 1}, {kate, 1}], fun(Alice, Bob, Kate) -> 715: ConfigChange = [{<<"roomname">>, <<"The Coven">>}], 716: escalus:send(Alice, stanza_config_set(?ROOM, ConfigChange)), 717: foreach_recipient([Alice, Bob, Kate], config_msg_verify_fun(ConfigChange)), 718: escalus:assert(is_iq_result, escalus:wait_for_stanza(Alice)) 719: end). 720: 721: set_config_with_custom_schema(Config) -> 722: escalus:story(Config, [{alice, 1}, {bob, 1}, {kate, 1}], fun(Alice, Bob, Kate) -> 723: ConfigChange = [{<<"background">>, <<"builtin:unicorns">>}, 724: {<<"music">>, <<"builtin:rainbow">>}], 725: escalus:send(Alice, stanza_config_set(?ROOM, ConfigChange)), 726: foreach_recipient([Alice, Bob, Kate], config_msg_verify_fun(ConfigChange)), 727: escalus:assert(is_iq_result, escalus:wait_for_stanza(Alice)) 728: end). 729: 730: deny_config_change_that_conflicts_with_schema(Config) -> 731: escalus:story(Config, [{alice, 1}], fun(Alice) -> 732: ConfigChange = [{<<"roomname">>, <<"The Coven">>}], 733: escalus:send(Alice, stanza_config_set(?ROOM, ConfigChange)), 734: escalus:assert(is_error, [<<"modify">>, <<"bad-request">>], 735: escalus:wait_for_stanza(Alice)) 736: end). 737: 738: assorted_config_doesnt_lead_to_duplication(Config) -> 739: escalus:story(Config, [{alice, 1}, {bob, 1}, {kate, 1}], fun(Alice, Bob, Kate) -> 740: ConfigChange = [{<<"subject">>, <<"Elixirs">>}, 741: {<<"roomname">>, <<"The Coven">>}, 742: {<<"subject">>, <<"Elixirs">>}], 743: ConfigSetStanza = stanza_config_set(?ROOM, ConfigChange), 744: escalus:send(Alice, ConfigSetStanza), 745: foreach_recipient([Alice, Bob, Kate], config_msg_verify_fun(ConfigChange)), 746: escalus:assert(is_iq_result, escalus:wait_for_stanza(Alice)), 747: 748: Stanza = stanza_config_get(?ROOM, <<"oldver">>), 749: VerifyFun = fun(Incoming) -> 750: [Query] = exml_query:subelements(Incoming, <<"query">>), 751: Length = length(Query#xmlel.children), 752: Length = length(lists:ukeysort(#xmlel.name, Query#xmlel.children)) 753: end, 754: foreach_occupant([Alice, Bob, Kate], Stanza, VerifyFun) 755: end). 756: 757: remove_and_add_users(Config) -> 758: escalus:story(Config, [{alice, 1}, {bob, 1}, {kate, 1}], fun(Alice, Bob, Kate) -> 759: AffUsersChanges1 = [{Bob, none}, {Kate, none}], 760: escalus:send(Alice, stanza_aff_set(?ROOM, AffUsersChanges1)), 761: verify_aff_bcast([{Alice, owner}], AffUsersChanges1), 762: escalus:assert(is_iq_result, escalus:wait_for_stanza(Alice)), 763: AffUsersChanges2 = [{Bob, member}, {Kate, member}], 764: escalus:send(Alice, stanza_aff_set(?ROOM, AffUsersChanges2)), 765: verify_aff_bcast([{Alice, owner}, {Bob, member}, {Kate, member}], AffUsersChanges2), 766: escalus:assert(is_iq_result, escalus:wait_for_stanza(Alice)) 767: end). 768: 769: explicit_owner_change(Config) -> 770: escalus:story(Config, [{alice, 1}, {bob, 1}, {kate, 1}], fun(Alice, Bob, Kate) -> 771: AffUsersChanges1 = [{Bob, none}, {Alice, none}, {Kate, owner}], 772: escalus:send(Alice, stanza_aff_set(?ROOM, AffUsersChanges1)), 773: verify_aff_bcast([{Kate, owner}], AffUsersChanges1), 774: escalus:assert(is_iq_result, escalus:wait_for_stanza(Alice)) 775: end). 776: 777: implicit_owner_change(Config) -> 778: escalus:story(Config, [{alice, 1}, {bob, 1}, {kate, 1}], fun(Alice, Bob, Kate) -> 779: AffUsersChanges1 = [{Bob, none}, {Alice, member}], 780: escalus:send(Alice, stanza_aff_set(?ROOM, AffUsersChanges1)), 781: verify_aff_bcast([{Kate, owner}, {Alice, member}], [{Kate, owner} | AffUsersChanges1]), 782: escalus:assert(is_iq_result, escalus:wait_for_stanza(Alice)) 783: end). 784: 785: edge_case_owner_change(Config) -> 786: escalus:story(Config, [{alice, 1}, {bob, 1}, {kate, 1}], fun(Alice, Bob, Kate) -> 787: AffUsersChanges1 = [{Alice, member}, {Bob, none}, {Kate, none}], 788: escalus:send(Alice, stanza_aff_set(?ROOM, AffUsersChanges1)), 789: verify_aff_bcast([{Alice, owner}], [{Kate, none}, {Bob, none}]), 790: escalus:assert(is_iq_result, escalus:wait_for_stanza(Alice)) 791: end). 792: 793: adding_wrongly_named_user_triggers_infinite_loop(Config)-> 794: escalus:story(Config, [{alice, 1}], fun(Alice) -> 795: BuggyRoomName = <<"buggyroom">>, 796: Username = <<"buggyuser">>, 797: escalus:send(Alice, generate_buggy_aff_staza(BuggyRoomName, Username)), 798: timer:sleep(300), 799: AUsername = lbin(escalus_users:get_username(Config, alice)), 800: Host = lbin(escalus_users:get_server(Config, alice)), 801: Resource = <<"res1">>, 802: JID = mongoose_helper:make_jid(AUsername, Host, Resource), 803: ct:log("JID ~p", [JID]), 804: SessionRecPid = rpc(ejabberd_sm, get_session, [JID]), 805: {session, {_,Pid}, {AUsername, Host, Resource}, _, _, _} = SessionRecPid, 806: %% maybe throws exception 807: assert_process_memory_not_growing(Pid, 0, 2), 808: escalus:wait_for_stanzas(Alice, 2) 809: end). 810: 811: %% ---------------------- limits ---------------------- 812: 813: rooms_per_user(Config) -> 814: escalus:story(Config, [{alice, 1}, {bob, 1}, {kate, 1}, {mike, 1}], 815: fun(Alice, Bob, Kate, Mike) -> 816: escalus:send(Bob, stanza_create_room(undefined, [], [])), 817: escalus:assert(is_error, [<<"modify">>, <<"bad-request">>], 818: escalus:wait_for_stanza(Bob)), 819: 820: escalus:send(Mike, stanza_create_room(<<"mikeroom">>, [], [])), 821: verify_aff_bcast([{Mike, owner}], [{Mike, owner}]), 822: escalus:assert(is_iq_result, escalus:wait_for_stanza(Mike)), 823: KateAdd = [{Kate, member}], 824: escalus:send(Mike, stanza_aff_set(<<"mikeroom">>, KateAdd)), 825: %% Receives result and nothing happens, because Kate's limit is reached 826: escalus:assert(is_iq_result, escalus:wait_for_stanza(Mike)), 827: 828: verify_no_stanzas([Alice, Bob, Kate, Mike]) 829: end). 830: 831: max_occupants(Config) -> 832: escalus:story(Config, [{alice, 1}, {bob, 1}, {kate, 1}, {mike, 1}], 833: fun(Alice, Bob, Kate, Mike) -> 834: escalus:send(Bob, stanza_create_room(undefined, [], [{Alice, member}, {Kate, member}])), 835: escalus:assert(is_error, [<<"modify">>, <<"bad-request">>], 836: escalus:wait_for_stanza(Bob)), 837: 838: MikeAdd = [{Mike, member}], 839: escalus:send(Alice, stanza_aff_set(?ROOM, MikeAdd)), 840: escalus:assert(is_error, [<<"modify">>, <<"bad-request">>], 841: escalus:wait_for_stanza(Alice)), 842: 843: verify_no_stanzas([Alice, Bob, Kate, Mike]) 844: end). 845: 846: %% ---------------------- blocking ---------------------- 847: 848: manage_blocklist(Config) -> 849: escalus:story(Config, [{alice, 1}], fun(Alice) -> 850: escalus:send(Alice, stanza_blocking_get()), 851: GetResult1 = escalus:wait_for_stanza(Alice), 852: escalus:assert(is_iq_result, GetResult1), 853: QueryEl1 = exml_query:subelement(GetResult1, <<"query">>), 854: verify_blocklist(QueryEl1, []), 855: Domain = domain(), 856: BlocklistChange1 = [{user, deny, <<"user@", Domain/binary>>}, 857: {room, deny, room_bin_jid(?ROOM)}], 858: escalus:send(Alice, stanza_blocking_set(BlocklistChange1)), 859: escalus:assert(is_iq_result, escalus:wait_for_stanza(Alice)), 860: escalus:send(Alice, stanza_blocking_get()), 861: GetResult2 = escalus:wait_for_stanza(Alice), 862: escalus:assert(is_iq_result, GetResult2), 863: QueryEl2 = exml_query:subelement(GetResult2, <<"query">>), 864: verify_blocklist(QueryEl2, BlocklistChange1), 865: 866: BlocklistChange2 = [{user, allow, <<"user@", Domain/binary>>}, 867: {room, allow, room_bin_jid(?ROOM)}], 868: escalus:send(Alice, stanza_blocking_set(BlocklistChange2)), 869: escalus:assert(is_iq_result, escalus:wait_for_stanza(Alice)), 870: escalus:send(Alice, stanza_blocking_get()), 871: GetResult3 = escalus:wait_for_stanza(Alice), 872: escalus:assert(is_iq_result, GetResult3), 873: % Match below checks for empty list 874: QueryEl1 = exml_query:subelement(GetResult3, <<"query">>) 875: end). 876: 877: block_room(Config) -> 878: escalus:story(Config, [{alice, 1}, {bob, 1}, {kate, 1}], fun(Alice, Bob, Kate) -> 879: BlocklistChange = [{room, deny, room_bin_jid(?ROOM)}], 880: escalus:send(Bob, stanza_blocking_set(BlocklistChange)), 881: escalus:assert(is_iq_result, escalus:wait_for_stanza(Bob)), 882: user_leave(?ROOM, Bob, [{Alice, owner}, {Kate, member}]), 883: 884: % Alice tries to readd Bob to the room but fails 885: BobReadd = [{Bob, member}], 886: FailStanza = stanza_aff_set(?ROOM, BobReadd), 887: escalus:send(Alice, FailStanza), 888: escalus:assert(is_iq_result, escalus:wait_for_stanza(Alice)), 889: verify_no_stanzas([Alice, Bob, Kate]), 890: 891: % But Alice can add Bob to another room! 892: InitOccupants = [{Bob, member}], 893: escalus:send(Alice, stanza_create_room(<<"newroom">>, [], InitOccupants)), 894: verify_aff_bcast([{Alice, owner}, {Bob, member}], 895: [{Alice, owner} | InitOccupants]), 896: escalus:assert(is_iq_result, escalus:wait_for_stanza(Alice)) 897: end). 898: 899: block_user(Config) -> 900: escalus:story(Config, [{alice, 1}, {bob, 1}, {kate, 1}], fun(Alice, Bob, Kate) -> 901: AliceJIDBin = lbin(escalus_client:short_jid(Alice)), 902: BlocklistChange = [{user, deny, AliceJIDBin}], 903: escalus:send(Bob, stanza_blocking_set(BlocklistChange)), 904: escalus:assert(is_iq_result, escalus:wait_for_stanza(Bob)), 905: user_leave(?ROOM, Bob, [{Alice, owner}, {Kate, member}]), 906: 907: % Alice tries to create new room with Bob but Bob is not added 908: escalus:send(Alice, stanza_create_room(<<"new">>, [], [{Bob, member}])), 909: verify_aff_bcast([{Alice, owner}], [{Alice, owner}]), 910: escalus:assert(is_iq_result, escalus:wait_for_stanza(Alice)), 911: verify_no_stanzas([Alice, Bob, Kate]), 912: 913: % But Kate can add Bob to the main room! 914: BobReadd = [{Bob, member}], 915: SuccessStanza = stanza_aff_set(?ROOM, BobReadd), 916: escalus:send(Kate, SuccessStanza), 917: verify_aff_bcast([{Alice, owner}, {Bob, member}, {Kate, member}], BobReadd), 918: escalus:assert(is_iq_result, escalus:wait_for_stanza(Kate)), 919: verify_no_stanzas([Alice, Bob, Kate]) 920: 921: end). 922: 923: blocking_disabled(Config) -> 924: escalus:story(Config, [{alice, 1}], fun(Alice) -> 925: escalus:send(Alice, stanza_blocking_get()), 926: escalus:assert(is_error, [<<"modify">>, <<"bad-request">>], 927: escalus:wait_for_stanza(Alice)), 928: Domain = domain(), 929: BlocklistChange1 = [{user, deny, <<"user@", Domain/binary>>}, 930: {room, deny, room_bin_jid(?ROOM)}], 931: escalus:send(Alice, stanza_blocking_set(BlocklistChange1)), 932: escalus:assert(is_error, [<<"modify">>, <<"bad-request">>], 933: escalus:wait_for_stanza(Alice)) 934: end). 935: 936: %%-------------------------------------------------------------------- 937: %% Subroutines 938: %%-------------------------------------------------------------------- 939: 940: -spec get_disco_rooms(User :: escalus:client()) -> list(xmlel()). 941: get_disco_rooms(User) -> 942: DiscoStanza = escalus_stanza:to(escalus_stanza:iq_get(?NS_DISCO_ITEMS, []), ?MUCHOST), 943: escalus:send(User, DiscoStanza), 944: Stanza = escalus:wait_for_stanza(User), 945: XNamespaces = exml_query:paths(Stanza, [{element, <<"query">>}, {attr, <<"xmlns">>}]), 946: true = lists:member(?NS_DISCO_ITEMS, XNamespaces), 947: escalus:assert(is_stanza_from, [?MUCHOST], Stanza), 948: exml_query:paths(Stanza, [{element, <<"query">>}, {element, <<"item">>}]). 949: 950: -spec generate_buggy_aff_staza(RoomName :: binary(), Username :: binary()) -> xmlel(). 951: generate_buggy_aff_staza(RoomName, Username) -> 952: BuggyJid = <<Username/binary, "@muclight.localhost">>, 953: BuggyUser = #client{jid = BuggyJid}, 954: stanza_create_room(RoomName, [], [{BuggyUser, member}]). 955: 956: %%-------------------------------------------------------------------- 957: %% IQ getters 958: %%-------------------------------------------------------------------- 959: 960: -spec stanza_blocking_get() -> xmlel(). 961: stanza_blocking_get() -> 962: escalus_stanza:to(escalus_stanza:iq_get(?NS_MUC_LIGHT_BLOCKING, []), ?MUCHOST). 963: 964: -spec stanza_config_get(Room :: binary(), Ver :: binary()) -> xmlel(). 965: stanza_config_get(Room, Ver) -> 966: escalus_stanza:to( 967: escalus_stanza:iq_get(?NS_MUC_LIGHT_CONFIGURATION, [version_el(Ver)]), room_bin_jid(Room)). 968: 969: -spec stanza_info_get(Room :: binary(), Ver :: binary()) -> xmlel(). 970: stanza_info_get(Room, Ver) -> 971: escalus_stanza:to( 972: escalus_stanza:iq_get(?NS_MUC_LIGHT_INFO, [version_el(Ver)]), room_bin_jid(Room)). 973: 974: -spec stanza_aff_get(Room :: binary(), Ver :: binary()) -> xmlel(). 975: stanza_aff_get(Room, Ver) -> 976: escalus_stanza:to( 977: escalus_stanza:iq_get(?NS_MUC_LIGHT_AFFILIATIONS, [version_el(Ver)]), room_bin_jid(Room)). 978: 979: %%-------------------------------------------------------------------- 980: %% IQ setters 981: %%-------------------------------------------------------------------- 982: 983: -spec stanza_destroy_room(Room :: binary()) -> xmlel(). 984: stanza_destroy_room(Room) -> 985: escalus_stanza:to(escalus_stanza:iq_set(?NS_MUC_LIGHT_DESTROY, []), room_bin_jid(Room)). 986: 987: -spec stanza_config_set(Room :: binary(), ConfigChanges :: [muc_light_helper:config_item()]) -> xmlel(). 988: stanza_config_set(Room, ConfigChanges) -> 989: Items = [ kv_el(Key, Value) || {Key, Value} <- ConfigChanges], 990: escalus_stanza:to( 991: escalus_stanza:iq_set(?NS_MUC_LIGHT_CONFIGURATION, Items), room_bin_jid(Room)). 992: 993: %%-------------------------------------------------------------------- 994: %% Verifiers 995: %%-------------------------------------------------------------------- 996: 997: -spec verify_blocklist(Query :: xmlel(), ProperBlocklist :: [ct_block_item()]) -> []. 998: verify_blocklist(Query, ProperBlocklist) -> 999: ?NS_MUC_LIGHT_BLOCKING = exml_query:attr(Query, <<"xmlns">>), 1000: BlockedRooms = exml_query:subelements(Query, <<"room">>), 1001: BlockedUsers = exml_query:subelements(Query, <<"user">>), 1002: BlockedItems = [{list_to_atom(binary_to_list(What)), list_to_atom(binary_to_list(Action)), Who} 1003: || #xmlel{name = What, attrs = [{<<"action">>, Action}], 1004: children = [#xmlcdata{ content = Who }]} 1005: <- BlockedRooms ++ BlockedUsers], 1006: ProperBlocklistLen = length(ProperBlocklist), 1007: ProperBlocklistLen = length(BlockedItems), 1008: [] = lists:foldl(fun lists:delete/2, BlockedItems, ProperBlocklist). 1009: 1010: -spec disco_items_verify_fun(list(Jid :: binary())) -> verify_fun(). 1011: disco_items_verify_fun(JidList) -> 1012: fun(Incomming) -> 1013: ResultItemList = exml_query:paths(Incomming, 1014: [{element, <<"query">>}, 1015: {element, <<"item">>}]), 1016: ResultJids = [exml_query:attr(ResultItem, <<"jid">>) || ResultItem <- ResultItemList], 1017: {SortedResult, SortedExptected} = {lists:sort(JidList), lists:sort(ResultJids)}, 1018: SortedResult = SortedExptected 1019: end. 1020: 1021: 1022: -spec verify_no_stanzas(Users :: [escalus:client()]) -> ok. 1023: verify_no_stanzas(Users) -> 1024: lists:foreach( 1025: fun(User) -> 1026: {false, _} = {escalus_client:has_stanzas(User), User} 1027: end, Users). 1028: 1029: -spec verify_config(ConfigRoot :: xmlel(), Config :: [muc_light_helper:config_item()]) -> ok. 1030: verify_config(ConfigRoot, Config) -> 1031: lists:foreach( 1032: fun({Key, Val}) -> 1033: Val = exml_query:path(ConfigRoot, [{element, Key}, cdata]) 1034: end, Config). 1035: 1036: %%-------------------------------------------------------------------- 1037: %% Verification funs generators 1038: %%-------------------------------------------------------------------- 1039: 1040: -spec config_msg_verify_fun(RoomConfig :: [muc_light_helper:config_item()]) -> verify_fun(). 1041: config_msg_verify_fun(RoomConfig) -> 1042: fun(Incoming) -> 1043: escalus:assert(is_groupchat_message, Incoming), 1044: [X] = exml_query:subelements(Incoming, <<"x">>), 1045: ?NS_MUC_LIGHT_CONFIGURATION = exml_query:attr(X, <<"xmlns">>), 1046: PrevVersion = exml_query:path(X, [{element, <<"prev-version">>}, cdata]), 1047: Version = exml_query:path(X, [{element, <<"version">>}, cdata]), 1048: true = is_binary(Version), 1049: true = is_binary(PrevVersion), 1050: true = Version =/= PrevVersion, 1051: lists:foreach( 1052: fun({Key, Val}) -> 1053: Val = exml_query:path(X, [{element, Key}, cdata]) 1054: end, RoomConfig) 1055: end. 1056: 1057: -spec config_iq_verify_fun(RoomConfig :: [muc_light_helper:config_item()]) -> verify_fun(). 1058: config_iq_verify_fun(RoomConfig) -> 1059: fun(Incoming) -> 1060: [Query] = exml_query:subelements(Incoming, <<"query">>), 1061: ?NS_MUC_LIGHT_CONFIGURATION = exml_query:attr(Query, <<"xmlns">>), 1062: verify_config(Query, RoomConfig) 1063: end. 1064: 1065: -spec aff_iq_verify_fun(AffUsers :: ct_aff_users(), Version :: binary()) -> verify_fun(). 1066: aff_iq_verify_fun(AffUsers, Version) -> 1067: BinAffUsers = bin_aff_users(AffUsers), 1068: fun(Incoming) -> 1069: [Query] = exml_query:subelements(Incoming, <<"query">>), 1070: ?NS_MUC_LIGHT_AFFILIATIONS = exml_query:attr(Query, <<"xmlns">>), 1071: Version = exml_query:path(Query, [{element, <<"version">>}, cdata]), 1072: Items = exml_query:subelements(Query, <<"user">>), 1073: verify_aff_users(Items, BinAffUsers) 1074: end. 1075: 1076: -spec info_iq_verify_fun(AffUsers :: ct_aff_users(), Version :: binary(), 1077: ConfigKV :: [muc_light_helper:config_item()]) -> verify_fun(). 1078: info_iq_verify_fun(AffUsers, Version, ConfigKV) -> 1079: BinAffUsers = bin_aff_users(AffUsers), 1080: fun(Incoming) -> 1081: [Query] = exml_query:subelements(Incoming, <<"query">>), 1082: ?NS_MUC_LIGHT_INFO = exml_query:attr(Query, <<"xmlns">>), 1083: Version = exml_query:path(Query, [{element, <<"version">>}, cdata]), 1084: UsersItems = exml_query:paths(Query, [{element, <<"occupants">>}, 1085: {element, <<"user">>}]), 1086: verify_aff_users(UsersItems, BinAffUsers), 1087: ConfigurationEl = exml_query:subelement(Query, <<"configuration">>), 1088: verify_config(ConfigurationEl, ConfigKV) 1089: end. 1090: 1091: -spec verify_user_has_one_room(User :: escalus:client()) -> any(). 1092: verify_user_has_one_room(User) -> 1093: [Item] = get_disco_rooms(User), 1094: ProperJID = room_bin_jid(?ROOM), 1095: ProperJID = exml_query:attr(Item, <<"jid">>). 1096: 1097: %%-------------------------------------------------------------------- 1098: %% Other helpers 1099: %%-------------------------------------------------------------------- 1100: 1101: -spec ver(Int :: integer()) -> binary(). 1102: ver(Int) -> 1103: <<"ver-", (list_to_binary(integer_to_list(Int)))/binary>>. 1104: 1105: -spec version_el(Version :: binary()) -> xmlel(). 1106: version_el(Version) -> 1107: #xmlel{ name = <<"version">>, children = [#xmlcdata{ content = Version }] }. 1108: 1109: -spec assert_process_memory_not_growing(pid(), integer(), integer()) -> any(). 1110: assert_process_memory_not_growing(_, _, Counter) when Counter > 4 -> 1111: throw({memory_consumption_is_growing}); 1112: assert_process_memory_not_growing(_, _, Counter) when Counter == 0 -> 1113: ok; 1114: assert_process_memory_not_growing(Pid, OldMemory, Counter) -> 1115: {memory, Memory} = rpc(erlang, process_info, [Pid, memory]), 1116: timer:sleep(1000), 1117: NewCounter = case Memory =< OldMemory of 1118: true -> 1119: Counter - 1; 1120: _ -> 1121: Counter + 1 1122: end, 1123: assert_process_memory_not_growing(Pid, Memory, NewCounter). 1124: 1125: -spec custom_schema() -> [muc_light_helper:schema_item()]. 1126: custom_schema() -> 1127: % This list needs to be sorted 1128: [{<<"background">>, <<"builtin:hell">>, background, binary}, 1129: {<<"music">>, <<"builtin:screams">>, music, binary}].