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