1: %%============================================================================== 2: %% Copyright 2013 Erlang Solutions Ltd. 3: %% 4: %% Licensed under the Apache License, Version 2.0 (the "License"); 5: %% you may not use this file except in compliance with the License. 6: %% You may obtain a copy of the License at 7: %% 8: %% http://www.apache.org/licenses/LICENSE-2.0 9: %% 10: %% Unless required by applicable law or agreed to in writing, software 11: %% distributed under the License is distributed on an "AS IS" BASIS, 12: %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13: %% See the License for the specific language governing permissions and 14: %% limitations under the License. 15: %%============================================================================== 16: -module(mongooseimctl_SUITE). 17: -compile([export_all, nowarn_export_all, nowarn_shadow_vars]). 18: -include_lib("escalus/include/escalus.hrl"). 19: -include_lib("common_test/include/ct.hrl"). 20: -include_lib("exml/include/exml.hrl"). 21: -include_lib("eunit/include/eunit.hrl"). 22: 23: -import(mongooseimctl_helper, [mongooseimctl/3, rpc_call/3]). 24: -import(mongoose_helper, [auth_modules/0]). 25: -import(distributed_helper, [mim/0, 26: require_rpc_nodes/1, 27: rpc/4]). 28: -import(domain_helper, [host_type/0, domain/0]). 29: -import(config_parser_helper, [config/2, default_mod_config/1]). 30: 31: -define(HTTP_UPLOAD_FILENAME, "tmp.txt"). 32: -define(HTTP_UPLOAD_FILESIZE, "5"). 33: -define(HTTP_UPLOAD_TIMEOUT, "666"). 34: -define(HTTP_UPLOAD_PARAMS(ContentType), ?HTTP_UPLOAD_PARAMS(?HTTP_UPLOAD_FILENAME, 35: ?HTTP_UPLOAD_FILESIZE, 36: ContentType, 37: ?HTTP_UPLOAD_TIMEOUT)). 38: -define(HTTP_UPLOAD_PARAMS_WITH_FILESIZE(X), ?HTTP_UPLOAD_PARAMS(?HTTP_UPLOAD_FILENAME, X, 39: "", ?HTTP_UPLOAD_TIMEOUT)). 40: -define(HTTP_UPLOAD_PARAMS_WITH_TIMEOUT(X), ?HTTP_UPLOAD_PARAMS(?HTTP_UPLOAD_FILENAME, 41: ?HTTP_UPLOAD_FILESIZE, "", X)). 42: -define(HTTP_UPLOAD_PARAMS(FileName, FileSize, ContentType, Timeout), 43: [domain(), FileName, FileSize, ContentType, Timeout]). 44: 45: -define(CTL_ERROR(Messsage), "Error: \"" ++ Messsage ++ "\"\n"). 46: -define(HTTP_UPLOAD_NOT_ENABLED_ERROR, ?CTL_ERROR("mod_http_upload is not loaded for this host")). 47: -define(HTTP_UPLOAD_FILESIZE_ERROR, ?CTL_ERROR("size must be positive integer")). 48: -define(HTTP_UPLOAD_TIMEOUT_ERROR, ?CTL_ERROR("timeout must be positive integer")). 49: 50: -define(S3_BUCKET_URL, "http://localhost:9000/mybucket/"). 51: -define(S3_REGION, "eu-east-25"). 52: -define(S3_ACCESS_KEY_ID, "AKIAIAOAONIULXQGMOUA"). 53: 54: %%Prefix MUST be a constant string, otherwise it results in compilation error 55: -define(GET_URL(Prefix, Sting), fun() -> Prefix ++ URL = Sting, URL end()). 56: 57: %% The following is an example presigned URL: 58: %% 59: %% https://s3.amazonaws.com/examplebucket/test.txt 60: %% ?X-Amz-Algorithm=AWS4-HMAC-SHA256 61: %% &X-Amz-Credential=<your-access-key-id>/20130721/us-east-1/s3/aws4_request 62: %% &X-Amz-Date=20130721T201207Z 63: %% &X-Amz-Expires=86400 64: %% &X-Amz-SignedHeaders=host 65: %% &X-Amz-Signature=<signature-value> 66: %% 67: %% for more details see 68: %% https://docs.aws.amazon.com/AmazonS3/latest/API/sigv4-query-string-auth.html 69: -define(S3_BASE_URL_REGEX, "^"?S3_BUCKET_URL".+/"?HTTP_UPLOAD_FILENAME). 70: -define(S3_ALGORITHM_REGEX, "[?&]X-Amz-Algorithm=AWS4-HMAC-SHA256(&|$)"). 71: -define(S3_CREDENTIAL_REGEX, 72: % X-Amz-Credential=<your-access-key-id>/<date>/<AWS-region>/<AWS-service>/aws4_request 73: "[?&]X-Amz-Credential="?S3_ACCESS_KEY_ID"%2F[0-9]{8}%2F"?S3_REGION"%2Fs3%2Faws4_request(&|$)"). 74: -define(S3_DATE_REGEX, "X-Amz-Date=[0-9]{8}T[0-9]{6}Z(&|$)"). 75: -define(S3_EXPIRATION_REGEX, "[?&]X-Amz-Expires="?HTTP_UPLOAD_TIMEOUT"(&|$)"). 76: -define(S3_SIGNED_HEADERS, "[?&]X-Amz-SignedHeaders=content-length%3Bhost(&|$)"). 77: -define(S3_SIGNED_HEADERS_WITH_ACL, 78: "[?&]X-Amz-SignedHeaders=content-length%3Bhost%3Bx-amz-acl(&|$)"). 79: -define(S3_SIGNED_HEADERS_WITH_CONTENT_TYPE, 80: "[?&]X-Amz-SignedHeaders=content-length%3Bcontent-type%3Bhost(&|$)"). 81: -define(S3_SIGNED_HEADERS_WITH_CONTENT_TYPE_AND_ACL, 82: "[?&]X-Amz-SignedHeaders=content-length%3Bcontent-type%3Bhost%3Bx-amz-acl(&|$)"). 83: -define(S3_SIGNATURE_REGEX, "[?&]X-Amz-Signature=[^&]+(&|$)"). 84: 85: 86: -record(offline_msg, {us, timestamp, expire, from, to, packet, permanent_fields = []}). 87: 88: %%-------------------------------------------------------------------- 89: %% Suite configuration 90: %%-------------------------------------------------------------------- 91: 92: all() -> 93: [ 94: {group, accounts}, 95: {group, sessions}, 96: {group, vcard}, 97: {group, roster}, 98: {group, roster_advanced}, 99: {group, last}, 100: {group, private}, 101: {group, stanza}, 102: {group, stats}, 103: {group, basic}, 104: {group, upload}, 105: {group, graphql} 106: ]. 107: 108: groups() -> 109: [{accounts, [sequence], accounts()}, 110: {sessions, [sequence], sessions()}, 111: {vcard, [sequence], vcard()}, 112: {roster, [sequence], roster()}, 113: {last, [sequence], last()}, 114: {private, [sequence], private()}, 115: {stanza, [sequence], stanza()}, 116: {roster_advanced, [sequence], roster_advanced()}, 117: {basic, [sequence], basic()}, 118: {stats, [sequence], stats()}, 119: {upload, [], upload()}, 120: {upload_with_acl, [], upload_enabled()}, 121: {upload_without_acl, [], upload_enabled()}, 122: {graphql, [], graphql()}]. 123: 124: basic() -> 125: [simple_register, 126: simple_unregister, 127: register_twice, 128: backup_restore_mnesia, 129: restore_mnesia_wrong, 130: dump_and_load, 131: load_mnesia_wrong, 132: dump_table, 133: get_loglevel, 134: remove_old_messages_test, 135: remove_expired_messages_test]. 136: 137: accounts() -> [change_password, check_password_hash, check_password, 138: check_account, ban_account, num_active_users, delete_old_users, 139: delete_old_users_vhost]. 140: 141: sessions() -> [num_resources_num, kick_session, status, 142: sessions_info, set_presence]. 143: 144: vcard() -> [vcard_rw, vcard2_rw, vcard2_multi_rw]. 145: 146: roster() -> [rosteritem_rw, 147: presence_after_add_rosteritem, 148: push_roster, 149: push_roster_all, 150: push_roster_alltoall]. 151: 152: roster_advanced() -> [process_rosteritems_list_simple, 153: process_rosteritems_list_nomatch, 154: process_rosteritems_list_advanced1, 155: process_rosteritems_list_advanced2, 156: process_rosteritems_delete_advanced, 157: process_rosteritems_delete_advanced2]. 158: 159: last() -> [set_last]. 160: 161: private() -> [private_rw]. 162: 163: stanza() -> [send_message, send_message_wrong_jid, send_stanza, send_stanzac2s_wrong]. 164: 165: stats() -> [stats_global, stats_host]. 166: 167: upload() -> 168: [upload_not_enabled, upload_wrong_filesize, upload_wrong_timeout, 169: {group, upload_with_acl}, {group, upload_without_acl}]. 170: 171: upload_enabled() -> 172: [upload_returns_correct_urls_without_content_type, 173: upload_returns_correct_urls_with_content_type, 174: real_upload_without_content_type, 175: real_upload_with_content_type]. 176: 177: graphql() -> 178: [graphql_wrong_arguments_number, 179: can_execute_admin_queries_with_permissions, 180: can_handle_execution_error]. 181: 182: suite() -> 183: require_rpc_nodes([mim]) ++ escalus:suite(). 184: 185: init_per_suite(Config) -> 186: TemplatePath = filename:join(?config(mim_data_dir, Config), "roster.template"), 187: AuthMods = auth_modules(), 188: Node = mim(), 189: Config1 = ejabberd_node_utils:init(Node, Config), 190: Config2 = escalus:init_per_suite([{ctl_auth_mods, AuthMods}, 191: {roster_template, TemplatePath} | Config1]), 192: dynamic_modules:ensure_modules(domain_helper:host_type(), [{mod_last, 193: default_mod_config(mod_last)}]), 194: dynamic_modules:ensure_modules(domain_helper:secondary_host_type(), 195: [{mod_last, default_mod_config(mod_last)}]), 196: prepare_roster_template(TemplatePath, domain()), 197: %% dump_and_load requires at least one mnesia table 198: %% ensure, that passwd table is available 199: catch rpc_call(ejabberd_auth_internal, start, [host_type()]), 200: escalus:create_users(Config2, escalus:get_users([alice, mike, bob, kate])). 201: 202: prepare_roster_template(TemplatePath, Domain) -> 203: {ok, [RosterIn]} = file:consult(TemplatePath ++ ".in"), 204: DomainStr = binary_to_list(Domain), 205: Roster = [{User, DomainStr, Group, Name} || {User, Group, Name} <- RosterIn], 206: FormattedRoster = io_lib:format("~tp.~n", [Roster]), 207: file:write_file(TemplatePath, FormattedRoster). 208: 209: end_per_suite(Config) -> 210: Config1 = lists:keydelete(ctl_auth_mods, 1, Config), 211: delete_users(Config1), 212: dynamic_modules:stop(domain_helper:host_type(), mod_last), 213: dynamic_modules:stop(domain_helper:secondary_host_type(), mod_last), 214: escalus:end_per_suite(Config1). 215: 216: init_per_group(basic, Config) -> 217: dynamic_modules:ensure_modules(domain_helper:host_type(), 218: [{mod_offline, default_mod_config(mod_offline)}]), 219: Config; 220: init_per_group(private, Config) -> 221: dynamic_modules:ensure_modules(domain_helper:host_type(), 222: [{mod_private, #{iqdisc => one_queue}}] 223: ), 224: Config; 225: init_per_group(vcard, Config) -> 226: case rpc(mim(), gen_mod, get_module_opt, [host_type(), mod_vcard, backend, mnesia]) of 227: ldap -> 228: {skip, vcard_set_not_supported_with_ldap}; 229: _ -> 230: Config 231: end; 232: init_per_group(roster_advanced, Config) -> 233: case rpc(mim(), gen_mod, get_module_opt, [host_type(), mod_roster, backend, mnesia]) of 234: mnesia -> 235: Config; 236: _ -> 237: {skip, command_process_rosteritems_supports_only_mnesia} 238: end; 239: init_per_group(upload_without_acl, Config) -> 240: dynamic_modules:start(host_type(), mod_http_upload, mod_http_upload_config(false)), 241: [{with_acl, false} | Config]; 242: init_per_group(upload_with_acl, Config) -> 243: dynamic_modules:start(host_type(), mod_http_upload, mod_http_upload_config(true)), 244: [{with_acl, true} | Config]; 245: init_per_group(_GroupName, Config) -> 246: Config. 247: 248: mod_http_upload_config(AddAcl) -> 249: config([modules, mod_http_upload], 250: #{max_file_size => 1234, 251: s3 => #{bucket_url => <<?S3_BUCKET_URL>>, 252: add_acl => AddAcl, 253: region => <<?S3_REGION>>, 254: access_key_id => <<?S3_ACCESS_KEY_ID>>, 255: secret_access_key => <<"CG5fGqG0/n6NCPJ10FylpdgRnuV52j8IZvU7BSj8">> 256: } 257: }). 258: 259: end_per_group(basic, Config) -> 260: dynamic_modules:stop(domain_helper:host_type(), mod_offline), 261: Config; 262: end_per_group(private, Config) -> 263: dynamic_modules:stop(domain_helper:host_type(), mod_private), 264: Config; 265: end_per_group(Rosters, Config) when (Rosters == roster) or (Rosters == roster_advanced) -> 266: TemplatePath = escalus_config:get_config(roster_template, Config), 267: RegUsers = [atom_to_list(U) || {U, _} <- escalus_config:get_config(escalus_users, Config)], 268: {ok, [Roster]} = file:consult(TemplatePath), 269: C = fun({U, S, _, _}) -> 270: case lists:member(U, RegUsers) of 271: true -> 272: SB = string_to_binary(S), 273: UB = string_to_binary(U), 274: Acc = mongoose_helper:new_mongoose_acc(SB), 275: rpc(mim(), mongoose_hooks, remove_user, [Acc, SB, UB]); 276: _ -> 277: ok 278: end 279: end, 280: lists:foreach(C, Roster), 281: Config; 282: end_per_group(UploadGroup, Config) when UploadGroup =:= upload_without_acl; 283: UploadGroup =:= upload_with_acl -> 284: dynamic_modules:stop(host_type(), mod_http_upload), 285: Config; 286: end_per_group(_GroupName, Config) -> 287: Config. 288: 289: get_registered_users() -> 290: rpc(mim(), ejabberd_auth, get_vh_registered_users, [domain()]). 291: 292: init_per_testcase(CaseName, Config) 293: when CaseName == delete_old_users_vhost 294: orelse CaseName == stats_global 295: orelse CaseName == stats_host -> 296: {_, AuthMods} = lists:keyfind(ctl_auth_mods, 1, Config), 297: case lists:member(ejabberd_auth_ldap, AuthMods) of 298: true -> {skip, "not supported for LDAP"}; 299: false -> escalus:init_per_testcase(CaseName, Config) 300: end; 301: init_per_testcase(check_password_hash, Config) -> 302: {_, AuthMods} = lists:keyfind(ctl_auth_mods, 1, Config), 303: case lists:member(ejabberd_auth_ldap, AuthMods) of 304: true -> 305: {skip, not_fully_supported_with_ldap}; 306: false -> 307: AuthOpts = mongoose_helper:auth_opts_with_password_format(plain), 308: Config1 = mongoose_helper:backup_and_set_config_option(Config, {auth, host_type()}, 309: AuthOpts), 310: Config2 = escalus:create_users(Config1, escalus:get_users([carol])), 311: escalus:init_per_testcase(check_password_hash, Config2) 312: end; 313: init_per_testcase(delete_old_users, Config) -> 314: {_, AuthMods} = lists:keyfind(ctl_auth_mods, 1, Config), 315: case lists:member(ejabberd_auth_ldap, AuthMods) of 316: true -> {skip, not_fully_supported_with_ldap}; 317: false -> escalus:init_per_testcase(delete_old_users, Config) 318: end; 319: init_per_testcase(CaseName, Config) when CaseName == real_upload_without_content_type; 320: CaseName == real_upload_with_content_type -> 321: case mongoose_helper:should_minio_be_running(Config) of 322: true -> escalus:init_per_testcase(CaseName, Config); 323: false -> {skip, "minio is not running"} 324: end; 325: init_per_testcase(CaseName, Config) -> 326: escalus:init_per_testcase(CaseName, Config). 327: 328: end_per_testcase(delete_old_users, Config) -> 329: Users = escalus_users:get_users([alice, bob, kate, mike]), 330: lists:foreach(fun({_User, UserSpec}) -> 331: {Username, Domain, Pass} = get_user_data(UserSpec, Config), 332: JID = mongoose_helper:make_jid(Username, Domain), 333: rpc(mim(), ejabberd_auth, try_register, [JID, Pass]) 334: end, Users), 335: escalus:end_per_testcase(delete_old_users, Config); 336: end_per_testcase(check_password_hash, Config) -> 337: mongoose_helper:restore_config(Config), 338: escalus:delete_users(Config, escalus:get_users([carol])); 339: end_per_testcase(CaseName, Config) -> 340: %% Because kick_session fails with unexpected stanza received: 341: %% <presence from="alicE@localhost/res3" 342: %% to="alice@localhost/res1" type="unavailable" /> 343: %% TODO: Remove when escalus learns how to automatically deal 344: %% with 'unavailable' stanzas on client stop. 345: mongoose_helper:kick_everyone(), 346: escalus:end_per_testcase(CaseName, Config). 347: 348: %%-------------------------------------------------------------------- 349: %% http upload tests 350: %%-------------------------------------------------------------------- 351: upload_not_enabled(Config) -> 352: Ret = mongooseimctl("http_upload", ?HTTP_UPLOAD_PARAMS("text/plain"), Config), 353: ?assertEqual({?HTTP_UPLOAD_NOT_ENABLED_ERROR, 1}, Ret). 354: 355: upload_wrong_filesize(Config) -> 356: Ret = mongooseimctl("http_upload", ?HTTP_UPLOAD_PARAMS_WITH_FILESIZE("0"), Config), 357: ?assertEqual({?HTTP_UPLOAD_FILESIZE_ERROR, 1}, Ret), 358: Ret = mongooseimctl("http_upload", ?HTTP_UPLOAD_PARAMS_WITH_FILESIZE("-1"), Config), 359: ?assertEqual({?HTTP_UPLOAD_FILESIZE_ERROR, 1}, Ret). 360: 361: upload_wrong_timeout(Config) -> 362: Ret = mongooseimctl("http_upload", ?HTTP_UPLOAD_PARAMS_WITH_TIMEOUT("0"), Config), 363: ?assertEqual({?HTTP_UPLOAD_TIMEOUT_ERROR, 1}, Ret), 364: Ret = mongooseimctl("http_upload", ?HTTP_UPLOAD_PARAMS_WITH_TIMEOUT("-1"), Config), 365: ?assertEqual({?HTTP_UPLOAD_TIMEOUT_ERROR, 1}, Ret). 366: 367: upload_returns_correct_urls_with_content_type(Config) -> 368: upload_returns_correct_urls(Config, "text/plain"). 369: 370: upload_returns_correct_urls_without_content_type(Config) -> 371: upload_returns_correct_urls(Config, ""). 372: 373: real_upload_with_content_type(Config) -> 374: real_upload(Config, "text/plain"). 375: 376: real_upload_without_content_type(Config) -> 377: real_upload(Config, ""). 378: 379: upload_returns_correct_urls(Config, ContentType) -> 380: HttpUploadParams = ?HTTP_UPLOAD_PARAMS(ContentType), 381: {Output, 0} = mongooseimctl("http_upload", HttpUploadParams, Config), 382: {PutURL, GetURL} = get_urls(Output), 383: WithACL = proplists:get_value(with_acl, Config), 384: check_urls(PutURL, GetURL, WithACL, ContentType). 385: 386: get_urls(Output) -> 387: [PutStr, GetStr | _] = string:split(Output, "\n", all), 388: PutURL = ?GET_URL("PutURL: ", PutStr), 389: GetURL = ?GET_URL("GetURL: ", GetStr), 390: {PutURL, GetURL}. 391: 392: check_urls(PutURL, GetURL, WithACL, ContentType) -> 393: check_bucket_url_and_filename(put, PutURL), 394: check_bucket_url_and_filename(get, GetURL), 395: check_substring(?S3_ALGORITHM_REGEX, PutURL), 396: check_substring(?S3_CREDENTIAL_REGEX, PutURL), 397: check_substring(?S3_DATE_REGEX, PutURL), 398: check_substring(?S3_EXPIRATION_REGEX, PutURL), 399: SignedHeadersRegex = signed_headers_regex(WithACL, ContentType), 400: check_substring(SignedHeadersRegex, PutURL), 401: check_substring(?S3_SIGNATURE_REGEX, PutURL). 402: 403: check_bucket_url_and_filename(Type, Url) -> 404: UrlRegex = case Type of 405: get -> ?S3_BASE_URL_REGEX"$"; 406: put -> ?S3_BASE_URL_REGEX"\?.*" 407: end, 408: ?assertMatch({match, [{0, _}]}, re:run(Url, UrlRegex)). 409: 410: check_substring(SubString, String) -> 411: ?assertMatch({match, [_]}, re:run(String, SubString, [global])). 412: 413: signed_headers_regex(false, "") -> ?S3_SIGNED_HEADERS; 414: signed_headers_regex(false, _) -> ?S3_SIGNED_HEADERS_WITH_CONTENT_TYPE; 415: signed_headers_regex(true, "") -> ?S3_SIGNED_HEADERS_WITH_ACL; 416: signed_headers_regex(true, _) -> ?S3_SIGNED_HEADERS_WITH_CONTENT_TYPE_AND_ACL. 417: 418: real_upload(Config, ContentType) -> 419: #{node := Node} = mim(), 420: BinPath = distributed_helper:bin_path(Node, Config), 421: UploadScript = filename:join(?config(mim_data_dir, Config), "test_file_upload.sh"), 422: Ret = mongooseimctl_helper:run(UploadScript, [ContentType], [{cd, BinPath}]), 423: ?assertMatch({_, 0}, Ret), 424: ok. 425: %%-------------------------------------------------------------------- 426: %% service_admin_extra_accounts tests 427: %%-------------------------------------------------------------------- 428: 429: change_password(Config) -> 430: {User, Domain, OldPassword} = get_user_data(alice, Config), 431: mongooseimctl("change_password", [User, Domain, <<OldPassword/binary, $2>>], Config), 432: {error, {connection_step_failed, _, _}} = escalus_client:start_for(Config, alice, <<"newres">>), 433: mongooseimctl("change_password", [User, Domain, OldPassword], Config), 434: {ok, Alice2} = escalus_client:start_for(Config, alice, <<"newres2">>), 435: escalus_client:stop(Config, Alice2). 436: 437: check_password_hash(Config) -> 438: {User, Domain, Pass} = get_user_data(carol, Config), 439: MD5Hash = get_md5(Pass), 440: MD5HashBad = get_md5(<<Pass/binary, "bad">>), 441: SHAHash = get_sha(Pass), 442: 443: {_, 0} = mongooseimctl("check_password_hash", [User, Domain, MD5Hash, "md5"], Config), 444: {_, ErrCode} = mongooseimctl("check_password_hash", [User, Domain, MD5HashBad, "md5"], Config), 445: true = (ErrCode =/= 0), %% Must return code other than 0 446: {_, 0} = mongooseimctl("check_password_hash", [User, Domain, SHAHash, "sha"], Config). 447: 448: check_password(Config) -> 449: {User, Domain, Pass} = get_user_data(alice, Config), 450: MetricName = [backends, auth, check_password], 451: OldValue = get_metric(MetricName), 452: {_, 0} = mongooseimctl("check_password", [User, Domain, Pass], Config), 453: {_, ErrCode} = mongooseimctl("check_password", [User, Domain, <<Pass/binary, "Bad">>], Config), 454: mongoose_helper:wait_until( 455: fun() -> get_metric(MetricName) end, true, 456: #{validator => fun(NewValue) -> OldValue =/= NewValue end, name => ?FUNCTION_NAME}), 457: true = (ErrCode =/= 0). %% Must return code other than 0 458: 459: get_metric(MetricName) -> 460: HostType = domain_helper:host_type(mim), 461: HostTypePrefix = domain_helper:make_metrics_prefix(HostType), 462: {ok, Value} = rpc(mim(), mongoose_metrics, get_metric_value, [[HostTypePrefix | MetricName]]), 463: Value. 464: 465: check_account(Config) -> 466: {User, Domain, _Pass} = get_user_data(alice, Config), 467: 468: {_, 0} = mongooseimctl("check_account", [User, Domain], Config), 469: {_, ErrCode} = mongooseimctl("check_account", [<<User/binary, "Bad">>, Domain], Config), 470: true = (ErrCode =/= 0). %% Must return code other than 0 471: 472: ban_account(Config) -> 473: {User, Domain, Pass} = get_user_data(mike, Config), 474: 475: {ok, Mike} = escalus_client:start_for(Config, mike, <<"newres">>), 476: {_, 0} = mongooseimctl("ban_account", [User, Domain, "SomeReason"], Config), 477: escalus:assert(is_stream_error, [<<"conflict">>, <<"SomeReason">>], 478: escalus:wait_for_stanza(Mike)), 479: {error, {connection_step_failed, _, _}} = escalus_client:start_for(Config, mike, <<"newres2">>), 480: mongooseimctl("change_password", [User, Domain, Pass], Config), 481: escalus_connection:wait_for_close(Mike, 1000), 482: escalus_cleaner:remove_client(Config, Mike). 483: 484: num_active_users(Config) -> 485: %% Given some users with recorded last activity timestamps 486: {AliceName, Domain, _} = get_user_data(alice, Config), 487: {MikeName, Domain, _} = get_user_data(mike, Config), 488: {Mega, Secs, _} = os:timestamp(), 489: Now = Mega * 1000000 + Secs, 490: set_last(AliceName, Domain, Now), 491: set_last(MikeName, Domain, Now), 492: {SLastActiveBefore, _} = mongooseimctl("num_active_users", [Domain, "5"], Config), 493: %% When we artificially remove a user's last activity timestamp in the given period 494: TenDaysAgo = Now - 864000, 495: set_last(MikeName, Domain, TenDaysAgo), 496: %% Then we expect that the number of active users in the last 5 days is one less 497: %% than before the change above 498: {SLastActiveAfter, _} = mongooseimctl("num_active_users", [Domain, "5"], Config), 499: NLastActiveBefore = list_to_integer(string:strip(SLastActiveBefore, both, $\n)), 500: NLastActiveAfter = list_to_integer(string:strip(SLastActiveAfter, both, $\n)), 501: NLastActiveAfter = NLastActiveBefore - 1. 502: 503: delete_old_users(Config) -> 504: {AliceName, Domain, _} = get_user_data(alice, Config), 505: {BobName, Domain, _} = get_user_data(bob, Config), 506: {KateName, Domain, _} = get_user_data(kate, Config), 507: {MikeName, Domain, _} = get_user_data(mike, Config), 508: 509: {Mega, Secs, _} = os:timestamp(), 510: Now = Mega*1000000+Secs, 511: set_last(AliceName, Domain, Now), 512: set_last(BobName, Domain, Now), 513: set_last(MikeName, Domain, Now), 514: set_last(KateName, Domain, 0), 515: 516: {_, 0} = mongooseimctl("delete_old_users", ["10"], Config), 517: {_, 0} = mongooseimctl("check_account", [AliceName, Domain], Config), 518: {_, ErrCode} = mongooseimctl("check_account", [KateName, Domain], Config), 519: true = (ErrCode =/= 0). %% Must return code other than 0 520: 521: delete_old_users_vhost(Config) -> 522: {AliceName, Domain, _} = get_user_data(alice, Config), 523: {KateName, Domain, KatePass} = get_user_data(kate, Config), 524: SecDomain = ct:get_config({hosts, mim, secondary_domain}), 525: 526: {Mega, Secs, _} = os:timestamp(), 527: Now = Mega*1000000+Secs, 528: set_last(AliceName, Domain, Now-86400*30), 529: 530: {_, 0} = mongooseimctl("register_identified", [KateName, SecDomain, KatePass], Config), 531: {_, 0} = mongooseimctl("check_account", [KateName, SecDomain], Config), 532: {_, 0} = mongooseimctl("delete_old_users_vhost", [SecDomain, "10"], Config), 533: {_, 0} = mongooseimctl("check_account", [AliceName, Domain], Config), 534: {_, ErrCode} = mongooseimctl("check_account", [KateName, SecDomain], Config), 535: true = (ErrCode =/= 0). %% Must return code other than 0 536: 537: %%-------------------------------------------------------------------- 538: %% service_admin_extra_accounts tests 539: %%-------------------------------------------------------------------- 540: 541: %% Checks both num_resources and resource_num 542: num_resources_num(Config) -> 543: escalus:story(Config, [{alice, 3}, {bob, 1}], fun(_, Alice2, _, _) -> 544: {Username, Domain, _} = get_user_data(alice, Config), 545: ResName = binary_to_list(escalus_client:resource(Alice2)) ++ "\n", 546: 547: {"3\n", _} = mongooseimctl("num_resources", [Username, Domain], Config), 548: {ResName, _} = mongooseimctl("resource_num", [Username, Domain, "2"], Config) 549: end). 550: 551: kick_session(Config) -> 552: escalus:story(Config, [{alice, 1}], fun(Alice) -> 553: Username = escalus_client:username(Alice), 554: Domain = escalus_client:server(Alice), 555: Resource = escalus_client:resource(Alice), 556: Args = [Username, Domain, Resource, "Because I can!"], 557: 558: {_, 0} = mongooseimctl("kick_session", Args, Config), 559: Stanza = escalus:wait_for_stanza(Alice), 560: escalus:assert(is_stream_error, [<<"conflict">>, <<"Because I can!">>], Stanza) 561: end). 562: 563: status(Config) -> 564: escalus:story(Config, [{alice, 1}, {mike, 1}, {bob, 1}], fun(User1, User2, User3) -> 565: PriDomain = escalus_client:server(User1), 566: SecDomain = ct:get_config({hosts, mim, secondary_domain}), 567: AwayPresence = escalus_stanza:presence_show(<<"away">>), 568: escalus_client:send(User2, AwayPresence), 569: 570: {"2\n", _} = mongooseimctl("status_num", ["available"], Config), 571: 572: {"2\n", _} = mongooseimctl("status_num_host", [PriDomain, "available"], Config), 573: {"0\n", _} = mongooseimctl("status_num_host", [SecDomain, "available"], Config), 574: 575: {StatusList, _} = mongooseimctl("status_list", ["available"], Config), 576: match_user_status([User1, User3], StatusList), 577: 578: {StatusList2, _} = mongooseimctl("status_list_host", 579: [PriDomain, "available"], Config), 580: match_user_status([User1, User3], StatusList2), 581: {[], _} = mongooseimctl("status_list_host", [SecDomain, "available"], Config) 582: end). 583: 584: sessions_info(Config) -> 585: escalus:story(Config, [{alice, 1}, {bob, 1}, {kate, 1}], fun(User1, User2, User3) -> 586: Username1 = escalus_client:username(User1), 587: PriDomain = escalus_client:server(User1), 588: SecDomain = ct:get_config({hosts, mim, secondary_domain}), 589: AwayPresence = escalus_stanza:presence_show(<<"away">>), 590: escalus_client:send(User2, AwayPresence), 591: 592: {UserList, _} = mongooseimctl("connected_users_info", [], Config), 593: match_user_info([User1, User2, User3], UserList), 594: 595: {UserList2, _} = mongooseimctl("connected_users_vhost", [PriDomain], Config), 596: match_user_info([User1, User2, User3], UserList2), 597: {[], _} = mongooseimctl("connected_users_vhost", [SecDomain], Config), 598: 599: {UserList3, _} = mongooseimctl("user_sessions_info", 600: [Username1, PriDomain], Config), 601: match_user_info([User1], UserList3) 602: end). 603: 604: set_presence(Config) -> 605: escalus:story(Config, [{alice, 1}], fun(Alice) -> 606: Username = escalus_client:username(Alice), 607: Domain = escalus_client:server(Alice), 608: Resource = escalus_client:resource(Alice), 609: 610: {_, 0} = mongooseimctl("set_presence", 611: [Username, Domain, Resource, 612: "available", "away", "mystatus", "10"], 613: Config), 614: Presence = escalus:wait_for_stanza(Alice), 615: escalus:assert(is_presence_with_show, [<<"away">>], Presence), 616: escalus:assert(is_presence_with_status, [<<"mystatus">>], Presence), 617: escalus:assert(is_presence_with_priority, [<<"10">>], Presence) 618: end). 619: 620: %%-------------------------------------------------------------------- 621: %% service_admin_extra_vcard tests 622: %%-------------------------------------------------------------------- 623: 624: vcard_rw(Config) -> 625: {Username, Domain, _} = get_user_data(alice, Config), 626: 627: {_, ExitCode} = mongooseimctl("get_vcard", [Username, Domain, "NICKNAME"], Config), 628: true = (ExitCode /= 0), 629: 630: {_, 0} = mongooseimctl("set_vcard", [Username, Domain, "NICKNAME", "SomeNickname"], Config), 631: {"SomeNickname\n", 0} = mongooseimctl("get_vcard", [Username, Domain, "NICKNAME"], Config). 632: 633: vcard2_rw(Config) -> 634: {Username, Domain, _} = get_user_data(alice, Config), 635: 636: {_, ExitCode} = mongooseimctl("get_vcard2", [Username, Domain, "ORG", "ORGNAME"], Config), 637: true = (ExitCode /= 0), 638: 639: {_, 0} = mongooseimctl("set_vcard2", [Username, Domain, "ORG", "ORGNAME", "ESL"], Config), 640: {"ESL\n", 0} = mongooseimctl("get_vcard2", [Username, Domain, "ORG", "ORGNAME"], Config). 641: 642: vcard2_multi_rw(Config) -> 643: {Username, Domain, _} = get_user_data(alice, Config), 644: 645: {_, ExitCode} = mongooseimctl("get_vcard2_multi", [Username, Domain, "ORG", "ORGUNIT"], Config), 646: true = (ExitCode /= 0), 647: 648: Args = [Username, Domain, "ORG", "ORGUNIT", "sales;marketing"], 649: {_, 0} = mongooseimctl("set_vcard2_multi", Args, Config), 650: {OrgUnits0, 0} = mongooseimctl("get_vcard2_multi", 651: [Username, Domain, "ORG", "ORGUNIT"], Config), 652: OrgUnits = string:tokens(OrgUnits0, "\n"), 653: 2 = length(OrgUnits), 654: true = (lists:member("sales", OrgUnits) andalso lists:member("marketing", OrgUnits)). 655: 656: %%-------------------------------------------------------------------- 657: %% service_admin_extra_vcard tests 658: %%-------------------------------------------------------------------- 659: 660: rosteritem_rw(Config) -> 661: escalus:story(Config, [{alice, 1}], fun(Alice) -> 662: BobJid = escalus_users:get_jid(Config, bob), 663: MikeJid = escalus_users:get_jid(Config, mike), 664: 665: {AliceName, Domain, _} = get_user_data(alice, Config), 666: {BobName, Domain, _} = get_user_data(bob, Config), 667: {MikeName, Domain, _} = get_user_data(mike, Config), 668: 669: {_, 0} = add_rosteritem1(AliceName, Domain, BobName, Config), 670: {_, 0} = mongooseimctl("add_rosteritem", 671: [AliceName, Domain, MikeName, 672: Domain, "My Mike", 673: "My Group", "both"], Config), 674: 675: [Push1, Push2] = escalus:wait_for_stanzas(Alice, 2), % Check roster broadcasts 676: escalus:assert(is_roster_set, Push1), 677: escalus:assert(roster_contains, [BobJid], Push1), 678: escalus:assert(is_roster_set, Push2), 679: escalus:assert(roster_contains, [MikeJid], Push2), 680: 681: {Items1, 0} = mongooseimctl("get_roster", [AliceName, Domain], Config), 682: match_roster([{BobName, Domain, "MyBob", "MyGroup", "both"}, 683: {MikeName, Domain, "MyMike", "MyGroup", "both"}], Items1), 684: 685: escalus:send(Alice, escalus_stanza:roster_get()), 686: Roster1 = escalus:wait_for_stanza(Alice), 687: escalus:assert(is_roster_result, Roster1), 688: escalus:assert(roster_contains, [BobJid], Roster1), 689: escalus:assert(roster_contains, [MikeJid], Roster1), 690: 691: {_, 0} = mongooseimctl("delete_rosteritem", 692: [AliceName, Domain, BobName, Domain], 693: Config), 694: 695: Push3 = escalus:wait_for_stanza(Alice), 696: escalus:assert(is_roster_set, Push3), 697: escalus:assert(roster_contains, [BobJid], Push3), 698: 699: {Items2, 0} = mongooseimctl("get_roster", [AliceName, Domain], Config), 700: match_roster([{MikeName, Domain, "MyMike", "MyGroup", "both"}], Items2), 701: 702: escalus:send(Alice, escalus_stanza:roster_remove_contact(MikeJid)), % cleanup 703: escalus:wait_for_stanzas(Alice, 2, 5000) 704: end). 705: 706: presence_after_add_rosteritem(Config) -> 707: escalus:story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) -> 708: BobJid = escalus_users:get_jid(Config, bob), 709: {AliceName, Domain, _} = get_user_data(alice, Config), 710: {BobName, Domain, _} = get_user_data(bob, Config), 711: 712: {_, 0} = add_rosteritem1(AliceName, Domain, BobName, Config), 713: 714: escalus:send(Alice, escalus_stanza:presence(<<"available">>)), 715: escalus:assert(is_presence, escalus:wait_for_stanza(Bob)), 716: 717: escalus:send(Alice, escalus_stanza:roster_remove_contact(BobJid)), % cleanup 718: %% Wait for stanzas, so they would not end up in the next story 719: escalus:wait_for_stanzas(Alice, 3, 5000) 720: end). 721: 722: push_roster(Config) -> 723: escalus:story(Config, [{alice, 1}], fun(Alice) -> 724: BobJid = escalus_users:get_jid(Config, bob), 725: {AliceName, Domain, _} = get_user_data(alice, Config), 726: TemplatePath = escalus_config:get_config(roster_template, Config), 727: 728: {_, 0} = mongooseimctl("push_roster", [TemplatePath, AliceName, Domain], Config), 729: escalus:send(Alice, escalus_stanza:roster_get()), 730: Roster1 = escalus:wait_for_stanza(Alice), 731: escalus:assert(is_roster_result, Roster1), 732: escalus:assert(roster_contains, [BobJid], Roster1), 733: 734: escalus:send(Alice, escalus_stanza:roster_remove_contact(BobJid)), % cleanup 735: escalus:wait_for_stanzas(Alice, 2, 5000) 736: end). 737: 738: process_rosteritems_list_simple(Config) -> 739: escalus:story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) -> 740: %% given 741: Action = "list", 742: Subs = "any", 743: Asks = "any", 744: User = escalus_client:short_jid(Alice), 745: Contact = string:to_lower(binary_to_list(escalus_client:short_jid(Bob))), 746: {AliceName, Domain, _} = get_user_data(alice, Config), 747: {BobName, Domain, _} = get_user_data(bob, Config), 748: %% when 749: {_, 0} = add_rosteritem1(AliceName, Domain, BobName, Config), 750: _S = escalus:wait_for_stanzas(Alice, 2), 751: {R, 0} = mongooseimctl("process_rosteritems", [Action, Subs, Asks, User, Contact], Config), 752: %% then 753: {match, _} = re:run(R, ".*Matches:.*" ++ Contact ++ ".*"), 754: {_, 0} = mongooseimctl("delete_rosteritem", [AliceName, Domain, BobName, Domain], Config) 755: end). 756: 757: process_rosteritems_list_nomatch(Config) -> 758: escalus:story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) -> 759: %% given 760: Action = "list", 761: Subs = "from:both", 762: Asks = "any", 763: User = escalus_client:short_jid(Alice), 764: Contact =string:to_lower(binary_to_list(escalus_client:short_jid(Bob))), 765: {AliceName, Domain, _} = get_user_data(alice, Config), 766: {BobName, Domain, _} = get_user_data(bob, Config), 767: {_, 0} = mongooseimctl("add_rosteritem", [AliceName, Domain, BobName, 768: Domain, "MyBob", "MyGroup", "to"], Config), 769: escalus:wait_for_stanzas(Alice, 2), 770: %% when 771: {R, 0} = mongooseimctl("process_rosteritems", [Action, Subs, Asks, User, Contact], Config), 772: %% then 773: nomatch = re:run(R, ".*Matches:.*" ++ Contact ++ ".*"), 774: {_, 0} = mongooseimctl("delete_rosteritem", [AliceName, Domain, BobName, Domain], Config) 775: end). 776: 777: process_rosteritems_list_advanced1(Config) -> 778: escalus:story(Config, [{alice, 1}, {mike, 1}, {kate, 1}], fun(Alice, Mike, Kate) -> 779: %% given 780: Action = "list", 781: Subs = "from:both", 782: Asks = "any", 783: User = escalus_client:short_jid(Alice), 784: {AliceName, Domain, _} = get_user_data(alice, Config), 785: {MikeName, Domain, _} = get_user_data(mike, Config), 786: {KateName, Domain, _} = get_user_data(kate, Config), 787: ContactMike = string:to_lower(binary_to_list(escalus_client:short_jid(Mike))), 788: ContactKate= string:to_lower(binary_to_list(escalus_client:short_jid(Kate))), 789: ContactsRegexp = ContactMike ++ ":" ++ 790: string:substr(binary_to_list(KateName), 1, 2) ++ 791: ".*@.*", 792: 793: {_, 0} = add_rosteritem2(AliceName, Domain, MikeName, Domain, Config), 794: {_, 0} = mongooseimctl("add_rosteritem", [AliceName, Domain, KateName, 795: Domain, "BestFriend", "MyGroup", "both"], Config), 796: escalus:wait_for_stanzas(Alice, 4), 797: %% when 798: {R, 0} = mongooseimctl("process_rosteritems", 799: [Action, Subs, Asks, User, ContactsRegexp], 800: Config), 801: %% then 802: {match, _} = re:run(R, ".*Matches:.*" ++ ContactMike ++ ".*"), 803: {match, _} = re:run(R, ".*Matches:.*" ++ ContactKate ++ ".*"), 804: {_, 0} = mongooseimctl("delete_rosteritem", [AliceName, Domain, MikeName, Domain], Config), 805: {_, 0} = mongooseimctl("delete_rosteritem", [AliceName, Domain, KateName, Domain], Config) 806: end). 807: 808: process_rosteritems_delete_advanced(Config) -> 809: escalus:story(Config, [{alice, 1}, {mike, 1}, {kate, 1}], fun(Alice, Mike, Kate) -> 810: %% given 811: Action = "delete", 812: Subs = "from", 813: Asks = "any", 814: User = escalus_client:short_jid(Alice), 815: {AliceName, Domain, _} = get_user_data(alice, Config), 816: {MikeName, Domain, _} = get_user_data(mike, Config), 817: {KateName, Domain, _} = get_user_data(kate, Config), 818: ContactMike = string:to_lower(binary_to_list(escalus_client:short_jid(Mike))), 819: ContactKate= string:to_lower(binary_to_list(escalus_client:short_jid(Kate))), 820: ContactsRegexp = ".*" ++ string:substr(ContactMike, 3) ++ 821: ":" ++ string:substr(ContactKate, 1, 2) ++ 822: "@" ++ binary_to_list(Domain), 823: {_, 0} = mongooseimctl("add_rosteritem", [AliceName, Domain, MikeName, 824: Domain, "DearMike", "MyGroup", "from"], Config), 825: {_, 0} = mongooseimctl("add_rosteritem", [AliceName, Domain, KateName, 826: Domain, "Friend", "MyGroup", "from"], Config), 827: escalus:wait_for_stanzas(Alice, 4), 828: %% when 829: {R, 0} = mongooseimctl("process_rosteritems", 830: [Action, Subs, Asks, User, ContactsRegexp], 831: Config), 832: %% then 833: {match, _} = re:run(R, ".*Matches:.*" ++ ContactMike ++ ".*"), 834: nomatch = re:run(R, ".*Matches:.*" ++ ContactKate ++ ".*"), 835: {_, 0} = mongooseimctl("delete_rosteritem", [AliceName, Domain, MikeName, Domain], Config), 836: {_, 0} = mongooseimctl("delete_rosteritem", [AliceName, Domain, KateName, Domain], Config) 837: end). 838: 839: process_rosteritems_list_advanced2(Config) -> 840: escalus:story(Config, [{alice, 1}, {mike, 1}, {kate, 1}], fun(Alice, Mike, Kate) -> 841: %% given 842: Action = "list", 843: Subs = "any", 844: Asks = "any", 845: User = escalus_client:short_jid(Alice), 846: {AliceName, Domain, _} = get_user_data(alice, Config), 847: {MikeName, Domain, _} = get_user_data(mike, Config), 848: {KateName, Domain, _} = get_user_data(kate, Config), 849: ContactMike = string:to_lower(binary_to_list(escalus_client:short_jid(Mike))), 850: ContactKate= string:to_lower(binary_to_list(escalus_client:short_jid(Kate))), 851: ContactsRegexp = ".*e@lo.*", 852: {_, 0} = add_rosteritem2(AliceName, Domain, MikeName, Domain, Config), 853: {_, 0} = mongooseimctl("add_rosteritem", [AliceName, Domain, KateName, 854: Domain, "KateFromSchool", 855: "MyGroup", "from"], Config), 856: escalus:wait_for_stanzas(Alice, 4), 857: %% when 858: {R, 0} = mongooseimctl("process_rosteritems", 859: [Action, Subs, Asks, User, ContactsRegexp], 860: Config), 861: %% then 862: {match, _} = re:run(R, ".*Matches:.*" ++ ContactMike ++ ".*"), 863: {match, _} = re:run(R, ".*Matches:.*" ++ ContactKate ++ ".*"), 864: {_, 0} = mongooseimctl("delete_rosteritem", [AliceName, Domain, MikeName, Domain], Config), 865: {_, 0} = mongooseimctl("delete_rosteritem", [AliceName, Domain, KateName, Domain], Config) 866: end). 867: 868: process_rosteritems_delete_advanced2(Config) -> 869: escalus:story(Config, [{alice, 1}, {bob, 1}, {mike, 1}, {kate, 1}], 870: fun(Alice, Bob, Mike, Kate) -> 871: %% given 872: Action = "delete", 873: Subs = "to:from", 874: Asks = "any", 875: User = "al.c[e]@.*host:((b[o]b)|(mike))@loc.*t2", 876: {AliceName, Domain, _} = get_user_data(alice, Config), 877: {BobName, Domain, _} = get_user_data(bob, Config), 878: {MikeName, Domain, _} = get_user_data(mike, Config), 879: {KateName, Domain, _} = get_user_data(kate, Config), 880: ContactMike = string:to_lower(binary_to_list(escalus_client:short_jid(Mike))), 881: ContactKate= string:to_lower(binary_to_list(escalus_client:short_jid(Kate))), 882: ContactBob= string:to_lower(binary_to_list(escalus_client:short_jid(Bob))), 883: ContactsReg = ".ik[ea]@localho+.*:k@loc.*st:(alice)+@.*:no", 884: {_, 0} = mongooseimctl("add_rosteritem", 885: [AliceName, Domain, MikeName, 886: Domain, "DearMike", "MyGroup", "to"], 887: Config), 888: {_, 0} = mongooseimctl("add_rosteritem", 889: [AliceName, Domain, KateName, 890: Domain, "HateHerSheHasSoNiceLegs", 891: "MyGroup", "to"], Config), 892: {_, 0} = mongooseimctl("add_rosteritem", [BobName, Domain, AliceName, 893: Domain, "Girlfriend", "MyGroup", "from"], Config), 894: escalus:wait_for_stanzas(Alice, 4), 895: escalus:wait_for_stanzas(Bob, 2), 896: %% when 897: {R, 0} = mongooseimctl("process_rosteritems", 898: [Action, Subs, Asks, User, ContactsReg], 899: Config), 900: %% then 901: {match, _} = re:run(R, ".*Matches:.*" ++ ContactMike ++ ".*"), 902: nomatch = re:run(R, ".*Matches:.*" ++ ContactKate ++ ".*"), 903: nomatch = re:run(R, ".*Matches:.*" ++ ContactBob ++ ".*"), 904: {_, 0} = mongooseimctl("delete_rosteritem", [AliceName, Domain, MikeName, Domain], Config), 905: {_, 0} = mongooseimctl("delete_rosteritem", [AliceName, Domain, KateName, Domain], Config), 906: {_, 0} = mongooseimctl("delete_rosteritem", [BobName, Domain, AliceName, Domain], Config) 907: end). 908: 909: push_roster_all(Config) -> 910: escalus:story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) -> 911: TemplatePath = escalus_config:get_config(roster_template, Config), 912: 913: {_, 0} = mongooseimctl("push_roster_all", [TemplatePath], Config), 914: 915: escalus:send(Alice, escalus_stanza:roster_get()), 916: Roster1 = escalus:wait_for_stanza(Alice), 917: escalus:assert(is_roster_result, Roster1), 918: BobJid = escalus_client:short_jid(Bob), 919: escalus:assert(roster_contains, [BobJid], Roster1), 920: 921: escalus:send(Bob, escalus_stanza:roster_get()), 922: Roster2 = escalus:wait_for_stanza(Bob), 923: escalus:assert(is_roster_result, Roster2), 924: AliceJid = escalus_client:short_jid(Alice), 925: escalus:assert(roster_contains, [AliceJid], Roster2), 926: 927: escalus:send_and_wait(Alice, escalus_stanza:roster_remove_contact(bob)), % cleanup 928: escalus:send_and_wait(Bob, escalus_stanza:roster_remove_contact(alice)) % cleanup 929: end). 930: 931: push_roster_alltoall(Config) -> 932: escalus:story(Config, [{alice, 1}], fun(Alice) -> 933: BobJid = escalus_users:get_jid(Config, bob), 934: MikeJid = escalus_users:get_jid(Config, mike), 935: KateJid = escalus_users:get_jid(Config, kate), 936: {_, Domain, _} = get_user_data(alice, Config), 937: 938: {_, 0} = mongooseimctl("push_roster_alltoall", [Domain, "MyGroup"], Config), 939: 940: escalus:send(Alice, escalus_stanza:roster_get()), 941: Roster = escalus:wait_for_stanza(Alice), 942: 943: escalus:assert(is_roster_result, Roster), 944: escalus:assert(roster_contains, [BobJid], Roster), 945: escalus:assert(roster_contains, [MikeJid], Roster), 946: escalus:assert(roster_contains, [KateJid], Roster) 947: end). 948: 949: %%-------------------------------------------------------------------- 950: %% service_admin_extra_last tests 951: %%-------------------------------------------------------------------- 952: 953: set_last(Config) -> 954: escalus:story(Config, [{alice, 1}], fun(Alice) -> 955: BobJid = escalus_users:get_jid(Config, bob), 956: {AliceName, Domain, _} = get_user_data(alice, Config), 957: {BobName, Domain, _} = get_user_data(bob, Config), 958: 959: {_, 0} = add_rosteritem1(AliceName, Domain, BobName, Config), 960: {_, 0} = mongooseimctl("add_rosteritem", 961: [BobName, Domain, AliceName, 962: Domain, "MyAlice", "MyGroup", "both"], 963: Config), 964: 965: escalus:wait_for_stanza(Alice), % ignore push 966: 967: Now = os:system_time(second), 968: TS = integer_to_list(Now - 7200), 969: {_, 0} = mongooseimctl("set_last", [BobName, Domain, TS, "Status"], Config), 970: escalus:send(Alice, escalus_stanza:last_activity(BobJid)), 971: LastAct = escalus:wait_for_stanza(Alice), 972: escalus:assert(is_last_result, LastAct), 973: Seconds = list_to_integer(binary_to_list( 974: exml_query:path(LastAct, [{element, <<"query">>}, 975: {attr, <<"seconds">>}]))), 976: true = (( (Seconds > 7100) andalso (Seconds < 7300) ) orelse Seconds), 977: 978: {_, 0} = mongooseimctl("delete_rosteritem", 979: [AliceName, Domain, BobName, Domain], 980: Config), % cleanup 981: {_, 0} = mongooseimctl("delete_rosteritem", 982: [BobName, Domain, AliceName, Domain], 983: Config) 984: end). 985: 986: %%-------------------------------------------------------------------- 987: %% service_admin_extra_private tests 988: %%-------------------------------------------------------------------- 989: 990: private_rw(Config) -> 991: {AliceName, Domain, _} = get_user_data(alice, Config), 992: XmlEl1 = "<secretinfo xmlns=\"nejmspejs\">1</secretinfo>", 993: XmlEl2 = "<secretinfo xmlns=\"inny\">2</secretinfo>", 994: 995: {_, 0} = mongooseimctl("private_set", [AliceName, Domain, XmlEl1], Config), 996: {_, 0} = mongooseimctl("private_set", [AliceName, Domain, XmlEl2], Config), 997: 998: {Result, 0} = mongooseimctl("private_get", 999: [AliceName, Domain, "secretinfo", "nejmspejs"], 1000: Config), 1001: {ok, #xmlel{ name = <<"secretinfo">>, attrs = [{<<"xmlns">>, <<"nejmspejs">>}], 1002: children = [#xmlcdata{ content = <<"1">> }]}} = exml:parse(list_to_binary(Result)). 1003: 1004: %%-------------------------------------------------------------------- 1005: %% service_admin_extra_stanza tests 1006: %%-------------------------------------------------------------------- 1007: 1008: send_message(Config) -> 1009: escalus:story(Config, [{alice, 1}, {bob, 2}], fun(Alice, Bob1, Bob2) -> 1010: {_, 0} = mongooseimctl("send_message_chat", [escalus_client:full_jid(Alice), 1011: escalus_client:full_jid(Bob1), 1012: "Hi Bob!"], Config), 1013: Stanza1 = escalus:wait_for_stanza(Bob1), 1014: escalus:assert(is_chat_message, [<<"Hi Bob!">>], Stanza1), 1015: 1016: {_, 0} = mongooseimctl("send_message_headline", 1017: [escalus_client:full_jid(Alice), 1018: escalus_client:short_jid(Bob1), 1019: "Subj", "Hi Bob!!"], Config), 1020: Stanza2 = escalus:wait_for_stanza(Bob1), 1021: Stanza3 = escalus:wait_for_stanza(Bob2), 1022: escalus:assert(is_headline_message, [<<"Subj">>, <<"Hi Bob!!">>], Stanza2), 1023: escalus:assert(is_headline_message, [<<"Subj">>, <<"Hi Bob!!">>], Stanza3) 1024: end). 1025: 1026: send_message_wrong_jid(Config) -> 1027: escalus:story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) -> 1028: {_, Err1} = mongooseimctl("send_message_chat", ["@@#$%!!.§§£", 1029: escalus_client:full_jid(Bob), 1030: "Hello bobby!"], Config), 1031: {_, Err2} = mongooseimctl("send_message_headline", ["%%@&@&@==//\///", 1032: escalus_client:short_jid(Bob), 1033: "Subj", "Are 1034: you there?"], 1035: Config), 1036: true = Err1 =/= 0, 1037: true = Err2 =/= 0, 1038: escalus_assert:has_no_stanzas(Alice), 1039: escalus_assert:has_no_stanzas(Bob) 1040: end). 1041: 1042: send_stanza(Config) -> 1043: escalus:story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) -> 1044: Domain = escalus_client:server(Alice), 1045: Resource = escalus_client:resource(Alice), 1046: {BobName, _, _} = get_user_data(bob, Config), 1047: BobJID = <<BobName/binary, $@, Domain/binary, $/, 1048: (escalus_client:resource(Bob))/binary>>, 1049: 1050: Stanza = Stanza = create_stanza(Alice, BobJID), 1051: {_, 0} = mongooseimctl("send_stanza_c2s", 1052: [BobName, Domain, Resource, Stanza], 1053: Config), 1054: 1055: Message = escalus:wait_for_stanza(Alice), 1056: escalus:assert(is_chat_message, [<<"Hi">>], Message), 1057: escalus:assert(is_stanza_from, [Bob], Message) 1058: end). 1059: 1060: send_stanzac2s_wrong(Config) -> 1061: escalus:story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) -> 1062: Domain = escalus_client:server(Alice), 1063: Resource = escalus_client:resource(Alice), 1064: WrongBobName = "bobby_the_great", 1065: {BobName, _, _} = get_user_data(bob, Config), 1066: BobJID = <<BobName/binary, $@, Domain/binary, $/, (escalus_client:resource(Bob))/binary>>, 1067: Stanza = create_stanza(Alice, BobJID), 1068: StanzaWrong = <<"<iq type='get' id='234234'><xmlns='wrongwrong'>">>, 1069: {_, Err} = mongooseimctl("send_stanza_c2s", 1070: [WrongBobName, Domain, Resource, Stanza], 1071: Config), 1072: {_, Err2} = mongooseimctl("send_stanza_c2s", 1073: [BobName, Domain, Resource, StanzaWrong], 1074: Config), 1075: 1076: true = Err =/= 0, 1077: true = Err2 =/= 0, 1078: escalus_assert:has_no_stanzas(Alice) 1079: end). 1080: 1081: create_stanza(Name1, JID2) -> 1082: exml:to_binary(escalus_stanza:from(escalus_stanza:chat_to(Name1, "Hi"), JID2)). 1083: 1084: %%-------------------------------------------------------------------- 1085: %% service_admin_extra_stats tests 1086: %%-------------------------------------------------------------------- 1087: 1088: stats_global(Config) -> 1089: escalus:story(Config, [{alice, 1}, {bob, 1}], fun(_Alice, _Bob) -> 1090: RegisteredCount = length(escalus_config:get_config(escalus_users, Config, [])), 1091: Registered = integer_to_list(RegisteredCount) ++ "\n", 1092: 1093: {UpTime, 0} = mongooseimctl("stats", ["uptimeseconds"], Config), 1094: _ = list_to_integer(string:strip(UpTime, both, $\n)), 1095: {Registered, 0} = mongooseimctl("stats", ["registeredusers"], Config), 1096: 1097: {"2\n", 0} = mongooseimctl("stats", ["onlineusersnode"], Config), 1098: 1099: {"2\n", 0} = mongooseimctl("stats", ["onlineusers"], Config) 1100: end). 1101: 1102: stats_host(Config) -> 1103: escalus:story(Config, [{alice, 1}, {bob, 1}], fun(Alice, _Bob) -> 1104: RegisteredCount = length(escalus_config:get_config(escalus_users, Config, [])), 1105: Registered = integer_to_list(RegisteredCount) ++ "\n", 1106: 1107: PriDomain = escalus_client:server(Alice), 1108: SecDomain = ct:get_config({hosts, mim, secondary_domain}), 1109: 1110: {Registered, 0} = mongooseimctl("stats_host", 1111: ["registeredusers", PriDomain], Config), 1112: {"0\n", 0} = mongooseimctl("stats_host", ["registeredusers", SecDomain], Config), 1113: 1114: {"2\n", 0} = mongooseimctl("stats_host", ["onlineusers", PriDomain], Config), 1115: {"0\n", 0} = mongooseimctl("stats_host", ["onlineusers", SecDomain], Config) 1116: end). 1117: 1118: %%-------------------------------------------------------------------- 1119: %% mongoose_graphql tests 1120: %%-------------------------------------------------------------------- 1121: 1122: can_execute_admin_queries_with_permissions(Config) -> 1123: Query = "query { checkAuth { authStatus } }", 1124: Res = mongooseimctl("graphql", [Query], Config), 1125: ?assertMatch({_, 0}, Res), 1126: Data = element(1, Res), 1127: ?assertNotEqual(nomatch, string:find(Data, "AUTHORIZED")). 1128: 1129: can_handle_execution_error(Config) -> 1130: Query = "{}", 1131: Res = mongooseimctl("graphql", [Query], Config), 1132: ?assertMatch({_, 0}, Res), 1133: Data = element(1, Res), 1134: ?assertNotEqual(nomatch, string:find(Data, "parser_error")). 1135: 1136: graphql_wrong_arguments_number(Config) -> 1137: ExpectedFragment = "This command requires", 1138: ResNoArgs = mongooseimctl("graphql", [], Config), 1139: ?assertMatch({_, 1}, ResNoArgs), 1140: Data1 = element(1, ResNoArgs), 1141: ?assertNotEqual(nomatch, string:find(Data1, ExpectedFragment)), 1142: 1143: ResTooManyArgs = mongooseimctl("graphql", ["{}", "{}"], Config), 1144: ?assertMatch({_, 1}, ResTooManyArgs), 1145: Data2 = element(1, ResTooManyArgs), 1146: ?assertNotEqual(nomatch, string:find(Data2, ExpectedFragment)). 1147: 1148: %%----------------------------------------------------------------- 1149: %% Improve coverage 1150: %%----------------------------------------------------------------- 1151: 1152: simple_register(Config) -> 1153: %% given 1154: Domain = domain(), 1155: {Name, Password} = {<<"tyler">>, <<"durden">>}, 1156: %% when 1157: {R1, 0} = mongooseimctl("registered_users", [Domain], Config), 1158: Before = length(string:tokens(R1, "\n")), 1159: {_, 0} = mongooseimctl("register", [Domain, Password], Config), 1160: {_, 0} = mongooseimctl("register_identified", [Name, Domain, Password], Config), 1161: 1162: {R2, 0} = mongooseimctl("registered_users", [Domain], Config), 1163: After = length(string:tokens(R2, "\n")), 1164: %% then 1165: 2 = After - Before. 1166: 1167: simple_unregister(Config) -> 1168: %% given 1169: Domain = domain(), 1170: {Name, _} = {<<"tyler">>, <<"durden">>}, 1171: %% when 1172: {_, 0} = mongooseimctl("unregister", [Name, Domain], Config), 1173: {R2, 0} = mongooseimctl("registered_users", [Domain], Config), 1174: %% then 1175: nomatch = re:run(R2, ".*(" ++ binary_to_list(Name) ++ ").*"). 1176: 1177: register_twice(Config) -> 1178: %% given 1179: Domain = domain(), 1180: {Name, Password} = {<<"tyler">>, <<"durden">>}, 1181: %% when 1182: {_, 0} = mongooseimctl("register_identified", [Name, Domain, Password], Config), 1183: {R, Code} = mongooseimctl("register_identified", [Name, Domain, Password], Config), 1184: %% then 1185: {match, _} = re:run(R, ".*(already registered).*"), 1186: true = (Code =/= 0), 1187: {_, 0} = mongooseimctl("unregister", [Name, Domain], Config). 1188: 1189: backup_restore_mnesia(Config) -> 1190: %% given 1191: TableName = passwd, 1192: TableSize = rpc_call(mnesia, table_info, [TableName, size]), 1193: %% Table passwd should not be empty 1194: FileName = "backup_mnesia.bup", 1195: %% when 1196: {R, 0} = mongooseimctl("backup", [FileName], Config), 1197: nomatch = re:run(R, ".+"), 1198: rpc_call(mnesia, clear_table, [TableName]), 1199: 0 = rpc_call(mnesia, table_info, [TableName, size]), 1200: {R2, 0} = mongooseimctl("restore", [FileName], Config), 1201: %% then 1202: nomatch = re:run(R2, ".+"), 1203: TableSize = rpc_call(mnesia, table_info, [TableName, size]). 1204: 1205: restore_mnesia_wrong(Config) -> 1206: FileName = "file that doesnt exist13123.bup", 1207: {R2, _} = mongooseimctl("restore", [FileName], Config), 1208: {match, Code} = re:run(R2, ".+"), 1209: true = (Code =/= 0). 1210: 1211: dump_and_load(Config) -> 1212: FileName = "dump.bup", 1213: TableName = passwd, 1214: %% Table passwd should not be empty 1215: TableSize = rpc_call(mnesia, table_info, [TableName, size]), 1216: {DumpReturns, 0} = mongooseimctl("dump", [FileName], Config), 1217: ct:log("DumpReturns ~p", [DumpReturns]), 1218: {ok, DumpData} = rpc_call(file, consult, [FileName]), 1219: ct:log("DumpData ~p", [DumpData]), 1220: rpc_call(mnesia, clear_table, [TableName]), 1221: 0 = rpc_call(mnesia, table_info, [TableName, size]), 1222: {R, 0} = mongooseimctl("load", [FileName], Config), 1223: ct:log("LoadReturns ~p", [R]), 1224: {match, _} = re:run(R, ".+"), 1225: TableSize = rpc_call(mnesia, table_info, [TableName, size]). 1226: 1227: load_mnesia_wrong(Config) -> 1228: FileName = "file that doesnt existRHCP.bup", 1229: {R2, Code} = mongooseimctl("restore", [FileName], Config), 1230: {match, _} = re:run(R2, ".+"), 1231: true = (Code =/= 0). 1232: 1233: dump_table(Config) -> 1234: FileName = "dump.mn", 1235: TableName = passwd, 1236: %% Table passwd should not be empty 1237: TableSize = rpc_call(mnesia, table_info, [TableName, size]), 1238: {_, 0} = mongooseimctl("dump_table", [FileName, atom_to_list(TableName)], Config), 1239: rpc_call(mnesia, clear_table, [TableName]), 1240: 0 = rpc_call(mnesia, table_info, [TableName, size]), 1241: {R, 0} = mongooseimctl("load", [FileName], Config), 1242: {match, _} = re:run(R, ".+"), 1243: TableSize = rpc_call(mnesia, table_info, [TableName, size]). 1244: 1245: get_loglevel(Config) -> 1246: {R, 0} = mongooseimctl("get_loglevel", [], Config), 1247: LogLevel = rpc_call(mongoose_logs, get_global_loglevel, []), 1248: Regexp = io_lib:format("global loglevel is \(.\)\{1,2\}, which means '~p'", [LogLevel]), 1249: {match, _} = re:run(R, Regexp, [{capture, first}]). 1250: 1251: remove_old_messages_test(Config) -> 1252: escalus:story(Config, [{alice, 1}], fun(_) -> 1253: %% given 1254: JidA = nick_to_jid(alice, Config), 1255: JidB = nick_to_jid(bob, Config), 1256: JidRecordAlice = jid:from_binary(JidA), 1257: JidRecordBob = jid:from_binary(JidB), 1258: Domain = domain(), 1259: Msg1 = escalus_stanza:chat_to(<<"bob@", Domain/binary>>, 1260: "Hi, how are you? Its old message!"), 1261: Msg2 = escalus_stanza:chat_to(<<"bob@", Domain/binary>>, 1262: "Hello its new message!"), 1263: OldTimestamp = fallback_timestamp(10, os:system_time(microsecond)), 1264: OfflineOld = generate_offline_message(JidRecordAlice, JidRecordBob, Msg1, OldTimestamp), 1265: OfflineNew = generate_offline_message(JidRecordAlice, JidRecordBob, Msg2, os:system_time(microsecond)), 1266: {LUser, LServer} = jid:to_lus(JidRecordBob), 1267: HostType = host_type(), 1268: rpc_call(mod_offline_backend, write_messages, [host_type(), LUser, LServer, [OfflineOld, OfflineNew]]), 1269: %% when 1270: {_, 0} = mongooseimctl("delete_old_messages", [LServer, "1"], Config), 1271: {ok, SecondList} = rpc_call(mod_offline_backend, pop_messages, [HostType, JidRecordBob]), 1272: %% then 1273: 1 = length(SecondList) 1274: end). 1275: 1276: remove_expired_messages_test(Config) -> 1277: escalus:story(Config, [{mike, 1}], fun(_) -> 1278: %% given 1279: JidA = nick_to_jid(mike, Config), 1280: JidB = nick_to_jid(kate, Config), 1281: JidRecordMike = jid:from_binary(JidA), 1282: JidRecordKate = jid:from_binary(JidB), 1283: Domain = domain(), 1284: Msg1 = escalus_stanza:chat_to(<<"kate@", Domain/binary>>, "Rolling stones"), 1285: Msg2 = escalus_stanza:chat_to(<<"kate@", Domain/binary>>, "Arctic monkeys!"), 1286: Msg3 = escalus_stanza:chat_to(<<"kate@", Domain/binary>>, "More wine..."), 1287: Msg4 = escalus_stanza:chat_to(<<"kate@", Domain/binary>>, "kings of leon"), 1288: OldTimestamp = fallback_timestamp(10, os:system_time(microsecond)), 1289: ExpirationTime = fallback_timestamp(2, os:system_time(microsecond)), 1290: ExpirationTimeFuture= fallback_timestamp(-5, os:system_time(microsecond)), 1291: OfflineOld = generate_offline_expired_message(JidRecordMike, 1292: JidRecordKate, Msg1, 1293: OldTimestamp, 1294: ExpirationTime), 1295: OfflineNow = generate_offline_expired_message(JidRecordMike, 1296: JidRecordKate, Msg2, os:system_time(microsecond), ExpirationTime), 1297: OfflineFuture = generate_offline_expired_message(JidRecordMike, 1298: JidRecordKate, Msg3, os:system_time(microsecond), ExpirationTimeFuture), 1299: OfflineFuture2 = generate_offline_expired_message(JidRecordMike, 1300: JidRecordKate, Msg4, 1301: OldTimestamp, 1302: ExpirationTimeFuture), 1303: {LUser, LServer} = jid:to_lus(JidRecordKate), 1304: Args = [OfflineOld, OfflineNow, OfflineFuture, OfflineFuture2], 1305: HostType = host_type(), 1306: rpc_call(mod_offline_backend, write_messages, [HostType, LUser, LServer, Args]), 1307: %% when 1308: {_, 0} = mongooseimctl("delete_expired_messages", [LServer], Config), 1309: {ok, SecondList} = rpc_call(mod_offline_backend, pop_messages, [HostType, JidRecordKate]), 1310: %% then 1311: 2 = length(SecondList) 1312: end). 1313: 1314: %%----------------------------------------------------------------- 1315: %% Helpers 1316: %%----------------------------------------------------------------- 1317: 1318: 1319: nick_to_jid(UserName, Config) when is_atom(UserName) -> 1320: UserSpec = escalus_users:get_userspec(Config, UserName), 1321: escalus_utils:jid_to_lower(escalus_users:get_jid(Config, UserSpec)). 1322: 1323: generate_offline_message(From, To, Msg, TimeStamp) -> 1324: {LUser, LServer} = jid:to_lus(To), 1325: #offline_msg{us = {LUser, LServer}, timestamp = TimeStamp, expire = never, 1326: from = From, to = To, packet = Msg}. 1327: 1328: generate_offline_expired_message(From, To, Msg, TimeStamp, ExpirationTime) -> 1329: {LUser, LServer} = jid:to_lus(To), 1330: #offline_msg{us = {LUser, LServer}, timestamp = TimeStamp, 1331: expire = ExpirationTime, from = From, to = To, packet = Msg}. 1332: 1333: 1334: fallback_timestamp(HowManyDays, TS_MicroSeconds) -> 1335: HowManySeconds = HowManyDays * 86400, 1336: HowManyMicroSeconds = erlang:convert_time_unit(HowManySeconds, second, microsecond), 1337: TS_MicroSeconds - HowManyMicroSeconds. 1338: 1339: get_user_data(User, Config) when is_atom(User) -> 1340: get_user_data(escalus_users:get_options(Config, User, <<"newres">>), Config); 1341: get_user_data(User, _Config) -> 1342: {_, Password} = lists:keyfind(password, 1, User), 1343: {_, Username} = lists:keyfind(username, 1, User), 1344: {_, Domain} = lists:keyfind(server, 1, User), 1345: {Username, Domain, Password}. 1346: 1347: get_md5(AccountPass) -> 1348: lists:flatten([io_lib:format("~.16B", [X]) 1349: || X <- binary_to_list(crypto:hash(md5, AccountPass))]). 1350: get_sha(AccountPass) -> 1351: lists:flatten([io_lib:format("~.16B", [X]) 1352: || X <- binary_to_list(crypto:hash(sha, AccountPass))]). 1353: 1354: set_last(User, Domain, TStamp) -> 1355: rpc(mim(), mod_last, store_last_info, 1356: [host_type(), escalus_utils:jid_to_lower(User), Domain, TStamp, <<>>]). 1357: 1358: delete_users(_Config) -> 1359: lists:foreach(fun({User, Domain}) -> 1360: JID = mongoose_helper:make_jid(User, Domain), 1361: rpc(mim(), ejabberd_auth, remove_user, [JID]) 1362: end, get_registered_users()). 1363: 1364: %%----------------------------------------------------------------- 1365: %% Predicates 1366: %%----------------------------------------------------------------- 1367: 1368: match_user_status(Users, StatusTxt) -> 1369: Statuses = string:tokens(StatusTxt, "\n"), 1370: true = (length(Users) == length(Statuses)), 1371: match_user_status2(Users, Statuses). 1372: 1373: match_user_status2([], _) -> 1374: true; 1375: match_user_status2([User | UserR], Statuses) -> 1376: Username = binary_to_list(escalus_client:username(User)), 1377: Domain = binary_to_list(escalus_client:server(User)), 1378: Resource = binary_to_list(escalus_client:resource(User)), 1379: 1380: true = lists:any(fun(Status) -> 1381: [Username, Domain, Resource] 1382: =:= 1383: lists:sublist(string:tokens(Status, "\t"), 1, 3) 1384: end, Statuses), 1385: match_user_status2(UserR, Statuses). 1386: 1387: match_user_info(Users, UsersTxt) -> 1388: UsersInfo = string:tokens(UsersTxt, "\n"), 1389: case length(Users) == length(UsersInfo) of 1390: true -> 1391: ok; 1392: false -> 1393: ct:fail(#{what => match_user_info_failed, 1394: users => Users, user_info => UsersInfo}) 1395: end, 1396: match_user_info2(Users, UsersInfo). 1397: 1398: match_user_info2([], _) -> 1399: true; 1400: match_user_info2([User | UserR], UsersInfo) -> 1401: Username = binary_to_list(escalus_client:username(User)), 1402: Domain = binary_to_list(escalus_client:server(User)), 1403: Resource = binary_to_list(escalus_client:resource(User)), 1404: FullJID = Username ++ "@" ++ Domain ++ "/" ++ Resource, 1405: 1406: true = lists:any(fun(UserInfo) -> 1407: string:str(UserInfo, string:to_lower(FullJID)) =:= 1 1408: end, UsersInfo), 1409: match_user_info2(UserR, UsersInfo). 1410: 1411: match_roster(ItemsValid, Items) -> 1412: ItemsTokens = [ string:tokens(ItemToken, "\t") || ItemToken <- string:tokens(Items, "\n") ], 1413: 1414: true = (length(ItemsValid) == length(ItemsTokens)), 1415: true = lists:all(fun({Username, Domain, _Nick, _Group, _Sub}) -> 1416: JID = escalus_utils:jid_to_lower(<<Username/binary, "@", Domain/binary >>), 1417: lists:any(fun 1418: ([RosterJID, _Nick, _Sub, "none", _Group]) -> 1419: JID =:= escalus_utils:jid_to_lower(list_to_binary(RosterJID)); 1420: (_) -> 1421: false 1422: end, ItemsTokens) 1423: end, ItemsValid). 1424: 1425: string_to_binary(List) -> 1426: case erlang:system_info(otp_release) of 1427: [$R|_] -> 1428: list_to_binary(List); 1429: _ -> 1430: unicode:characters_to_binary(List) 1431: end. 1432: 1433: add_rosteritem1(UserName1, Domain, UserName2, Config) -> 1434: mongooseimctl("add_rosteritem", 1435: [UserName1, Domain, UserName2, 1436: Domain, "MyBob", "MyGroup", "both"], Config). 1437: 1438: add_rosteritem2(Name1, Domain1, Name2, Domain2, Config) -> 1439: mongooseimctl("add_rosteritem", 1440: [Name1, Domain1, Name2, 1441: Domain2, "DearMike", "MyGroup", "both"], Config).