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