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: ct:log("check_bucket_url_and_filename type=~p url=~p regex=~p", 409: [Type, Url, UrlRegex]), 410: ?assertMatch({match, [{0, _}]}, re:run(Url, UrlRegex)). 411: 412: check_substring(SubString, String) -> 413: ?assertMatch({match, [_]}, re:run(String, SubString, [global])). 414: 415: signed_headers_regex(false, "") -> ?S3_SIGNED_HEADERS; 416: signed_headers_regex(false, _) -> ?S3_SIGNED_HEADERS_WITH_CONTENT_TYPE; 417: signed_headers_regex(true, "") -> ?S3_SIGNED_HEADERS_WITH_ACL; 418: signed_headers_regex(true, _) -> ?S3_SIGNED_HEADERS_WITH_CONTENT_TYPE_AND_ACL. 419: 420: real_upload(Config, ContentType) -> 421: #{node := Node} = mim(), 422: BinPath = distributed_helper:bin_path(Node, Config), 423: UploadScript = filename:join(?config(mim_data_dir, Config), "test_file_upload.sh"), 424: Ret = mongooseimctl_helper:run(UploadScript, [ContentType], [{cd, BinPath}]), 425: ?assertMatch({_, 0}, Ret), 426: ok. 427: %%-------------------------------------------------------------------- 428: %% service_admin_extra_accounts tests 429: %%-------------------------------------------------------------------- 430: 431: change_password(Config) -> 432: {User, Domain, OldPassword} = get_user_data(alice, Config), 433: mongooseimctl("change_password", [User, Domain, <<OldPassword/binary, $2>>], Config), 434: {error, {connection_step_failed, _, _}} = escalus_client:start_for(Config, alice, <<"newres">>), 435: mongooseimctl("change_password", [User, Domain, OldPassword], Config), 436: {ok, Alice2} = escalus_client:start_for(Config, alice, <<"newres2">>), 437: escalus_client:stop(Config, Alice2). 438: 439: check_password_hash(Config) -> 440: {User, Domain, Pass} = get_user_data(carol, Config), 441: MD5Hash = get_md5(Pass), 442: MD5HashBad = get_md5(<<Pass/binary, "bad">>), 443: SHAHash = get_sha(Pass), 444: 445: {_, 0} = mongooseimctl("check_password_hash", [User, Domain, MD5Hash, "md5"], Config), 446: {_, ErrCode} = mongooseimctl("check_password_hash", [User, Domain, MD5HashBad, "md5"], Config), 447: true = (ErrCode =/= 0), %% Must return code other than 0 448: {_, 0} = mongooseimctl("check_password_hash", [User, Domain, SHAHash, "sha"], Config). 449: 450: check_password(Config) -> 451: {User, Domain, Pass} = get_user_data(alice, Config), 452: MetricName = [backends, auth, check_password], 453: OldValue = get_metric(MetricName), 454: {_, 0} = mongooseimctl("check_password", [User, Domain, Pass], Config), 455: {_, ErrCode} = mongooseimctl("check_password", [User, Domain, <<Pass/binary, "Bad">>], Config), 456: mongoose_helper:wait_until( 457: fun() -> get_metric(MetricName) end, true, 458: #{validator => fun(NewValue) -> OldValue =/= NewValue end, name => ?FUNCTION_NAME}), 459: true = (ErrCode =/= 0). %% Must return code other than 0 460: 461: get_metric(MetricName) -> 462: HostType = domain_helper:host_type(mim), 463: HostTypePrefix = domain_helper:make_metrics_prefix(HostType), 464: {ok, Value} = rpc(mim(), mongoose_metrics, get_metric_value, [[HostTypePrefix | MetricName]]), 465: Value. 466: 467: check_account(Config) -> 468: {User, Domain, _Pass} = get_user_data(alice, Config), 469: 470: {_, 0} = mongooseimctl("check_account", [User, Domain], Config), 471: {_, ErrCode} = mongooseimctl("check_account", [<<User/binary, "Bad">>, Domain], Config), 472: true = (ErrCode =/= 0). %% Must return code other than 0 473: 474: ban_account(Config) -> 475: {User, Domain, Pass} = get_user_data(mike, Config), 476: 477: {ok, Mike} = escalus_client:start_for(Config, mike, <<"newres">>), 478: {_, 0} = mongooseimctl("ban_account", [User, Domain, "SomeReason"], Config), 479: escalus:assert(is_stream_error, [<<"conflict">>, <<"SomeReason">>], 480: escalus:wait_for_stanza(Mike)), 481: {error, {connection_step_failed, _, _}} = escalus_client:start_for(Config, mike, <<"newres2">>), 482: mongooseimctl("change_password", [User, Domain, Pass], Config), 483: escalus_connection:wait_for_close(Mike, 1000), 484: escalus_cleaner:remove_client(Config, Mike). 485: 486: num_active_users(Config) -> 487: %% Given some users with recorded last activity timestamps 488: {AliceName, Domain, _} = get_user_data(alice, Config), 489: {MikeName, Domain, _} = get_user_data(mike, Config), 490: {Mega, Secs, _} = os:timestamp(), 491: Now = Mega * 1000000 + Secs, 492: set_last(AliceName, Domain, Now), 493: set_last(MikeName, Domain, Now), 494: {SLastActiveBefore, _} = mongooseimctl("num_active_users", [Domain, "5"], Config), 495: %% When we artificially remove a user's last activity timestamp in the given period 496: TenDaysAgo = Now - 864000, 497: set_last(MikeName, Domain, TenDaysAgo), 498: %% Then we expect that the number of active users in the last 5 days is one less 499: %% than before the change above 500: {SLastActiveAfter, _} = mongooseimctl("num_active_users", [Domain, "5"], Config), 501: NLastActiveBefore = list_to_integer(string:strip(SLastActiveBefore, both, $\n)), 502: NLastActiveAfter = list_to_integer(string:strip(SLastActiveAfter, both, $\n)), 503: NLastActiveAfter = NLastActiveBefore - 1. 504: 505: delete_old_users(Config) -> 506: {AliceName, Domain, _} = get_user_data(alice, Config), 507: {BobName, Domain, _} = get_user_data(bob, Config), 508: {KateName, Domain, _} = get_user_data(kate, Config), 509: {MikeName, Domain, _} = get_user_data(mike, Config), 510: 511: {Mega, Secs, _} = os:timestamp(), 512: Now = Mega*1000000+Secs, 513: set_last(AliceName, Domain, Now), 514: set_last(BobName, Domain, Now), 515: set_last(MikeName, Domain, Now), 516: set_last(KateName, Domain, 0), 517: 518: {_, 0} = mongooseimctl("delete_old_users", ["10"], Config), 519: {_, 0} = mongooseimctl("check_account", [AliceName, Domain], Config), 520: {_, ErrCode} = mongooseimctl("check_account", [KateName, Domain], Config), 521: true = (ErrCode =/= 0). %% Must return code other than 0 522: 523: delete_old_users_vhost(Config) -> 524: {AliceName, Domain, _} = get_user_data(alice, Config), 525: {KateName, Domain, KatePass} = get_user_data(kate, Config), 526: SecDomain = ct:get_config({hosts, mim, secondary_domain}), 527: 528: {Mega, Secs, _} = os:timestamp(), 529: Now = Mega*1000000+Secs, 530: set_last(AliceName, Domain, Now-86400*30), 531: 532: {_, 0} = mongooseimctl("register_identified", [KateName, SecDomain, KatePass], Config), 533: {_, 0} = mongooseimctl("check_account", [KateName, SecDomain], Config), 534: {_, 0} = mongooseimctl("delete_old_users_vhost", [SecDomain, "10"], Config), 535: {_, 0} = mongooseimctl("check_account", [AliceName, Domain], Config), 536: {_, ErrCode} = mongooseimctl("check_account", [KateName, SecDomain], Config), 537: true = (ErrCode =/= 0). %% Must return code other than 0 538: 539: %%-------------------------------------------------------------------- 540: %% service_admin_extra_accounts tests 541: %%-------------------------------------------------------------------- 542: 543: %% Checks both num_resources and resource_num 544: num_resources_num(Config) -> 545: escalus:story(Config, [{alice, 3}, {bob, 1}], fun(_, Alice2, _, _) -> 546: {Username, Domain, _} = get_user_data(alice, Config), 547: ResName = binary_to_list(escalus_client:resource(Alice2)) ++ "\n", 548: 549: {"3\n", _} = mongooseimctl("num_resources", [Username, Domain], Config), 550: {ResName, _} = mongooseimctl("resource_num", [Username, Domain, "2"], Config) 551: end). 552: 553: kick_session(Config) -> 554: escalus:story(Config, [{alice, 1}], fun(Alice) -> 555: Username = escalus_client:username(Alice), 556: Domain = escalus_client:server(Alice), 557: Resource = escalus_client:resource(Alice), 558: Args = [Username, Domain, Resource, "Because I can!"], 559: 560: {_, 0} = mongooseimctl("kick_session", Args, Config), 561: Stanza = escalus:wait_for_stanza(Alice), 562: escalus:assert(is_stream_error, [<<"conflict">>, <<"Because I can!">>], Stanza) 563: end). 564: 565: status(Config) -> 566: escalus:story(Config, [{alice, 1}, {mike, 1}, {bob, 1}], fun(User1, User2, User3) -> 567: PriDomain = escalus_client:server(User1), 568: SecDomain = ct:get_config({hosts, mim, secondary_domain}), 569: AwayPresence = escalus_stanza:presence_show(<<"away">>), 570: escalus_client:send(User2, AwayPresence), 571: 572: {"2\n", _} = mongooseimctl("status_num", ["available"], Config), 573: 574: {"2\n", _} = mongooseimctl("status_num_host", [PriDomain, "available"], Config), 575: {"0\n", _} = mongooseimctl("status_num_host", [SecDomain, "available"], Config), 576: 577: {StatusList, _} = mongooseimctl("status_list", ["available"], Config), 578: match_user_status([User1, User3], StatusList), 579: 580: {StatusList2, _} = mongooseimctl("status_list_host", 581: [PriDomain, "available"], Config), 582: match_user_status([User1, User3], StatusList2), 583: {[], _} = mongooseimctl("status_list_host", [SecDomain, "available"], Config) 584: end). 585: 586: sessions_info(Config) -> 587: escalus:story(Config, [{alice, 1}, {bob, 1}, {kate, 1}], fun(User1, User2, User3) -> 588: Username1 = escalus_client:username(User1), 589: PriDomain = escalus_client:server(User1), 590: SecDomain = ct:get_config({hosts, mim, secondary_domain}), 591: AwayPresence = escalus_stanza:presence_show(<<"away">>), 592: escalus_client:send(User2, AwayPresence), 593: 594: {UserList, _} = mongooseimctl("connected_users_info", [], Config), 595: match_user_info([User1, User2, User3], UserList), 596: 597: {UserList2, _} = mongooseimctl("connected_users_vhost", [PriDomain], Config), 598: match_user_info([User1, User2, User3], UserList2), 599: {[], _} = mongooseimctl("connected_users_vhost", [SecDomain], Config), 600: 601: {UserList3, _} = mongooseimctl("user_sessions_info", 602: [Username1, PriDomain], Config), 603: match_user_info([User1], UserList3) 604: end). 605: 606: set_presence(Config) -> 607: escalus:story(Config, [{alice, 1}], fun(Alice) -> 608: Username = escalus_client:username(Alice), 609: Domain = escalus_client:server(Alice), 610: Resource = escalus_client:resource(Alice), 611: 612: {_, 0} = mongooseimctl("set_presence", 613: [Username, Domain, Resource, 614: "available", "away", "mystatus", "10"], 615: Config), 616: Presence = escalus:wait_for_stanza(Alice), 617: escalus:assert(is_presence_with_show, [<<"away">>], Presence), 618: escalus:assert(is_presence_with_status, [<<"mystatus">>], Presence), 619: escalus:assert(is_presence_with_priority, [<<"10">>], Presence) 620: end). 621: 622: %%-------------------------------------------------------------------- 623: %% service_admin_extra_vcard tests 624: %%-------------------------------------------------------------------- 625: 626: vcard_rw(Config) -> 627: {Username, Domain, _} = get_user_data(alice, Config), 628: 629: {_, ExitCode} = mongooseimctl("get_vcard", [Username, Domain, "NICKNAME"], Config), 630: true = (ExitCode /= 0), 631: 632: {_, 0} = mongooseimctl("set_vcard", [Username, Domain, "NICKNAME", "SomeNickname"], Config), 633: {"SomeNickname\n", 0} = mongooseimctl("get_vcard", [Username, Domain, "NICKNAME"], Config). 634: 635: vcard2_rw(Config) -> 636: {Username, Domain, _} = get_user_data(alice, Config), 637: 638: {_, ExitCode} = mongooseimctl("get_vcard2", [Username, Domain, "ORG", "ORGNAME"], Config), 639: true = (ExitCode /= 0), 640: 641: {_, 0} = mongooseimctl("set_vcard2", [Username, Domain, "ORG", "ORGNAME", "ESL"], Config), 642: {"ESL\n", 0} = mongooseimctl("get_vcard2", [Username, Domain, "ORG", "ORGNAME"], Config). 643: 644: vcard2_multi_rw(Config) -> 645: {Username, Domain, _} = get_user_data(alice, Config), 646: 647: {_, ExitCode} = mongooseimctl("get_vcard2_multi", [Username, Domain, "ORG", "ORGUNIT"], Config), 648: true = (ExitCode /= 0), 649: 650: Args = [Username, Domain, "ORG", "ORGUNIT", "sales;marketing"], 651: {_, 0} = mongooseimctl("set_vcard2_multi", Args, Config), 652: {OrgUnits0, 0} = mongooseimctl("get_vcard2_multi", 653: [Username, Domain, "ORG", "ORGUNIT"], Config), 654: OrgUnits = string:tokens(OrgUnits0, "\n"), 655: 2 = length(OrgUnits), 656: true = (lists:member("sales", OrgUnits) andalso lists:member("marketing", OrgUnits)). 657: 658: %%-------------------------------------------------------------------- 659: %% service_admin_extra_vcard tests 660: %%-------------------------------------------------------------------- 661: 662: rosteritem_rw(Config) -> 663: escalus:story(Config, [{alice, 1}], fun(Alice) -> 664: BobJid = escalus_users:get_jid(Config, bob), 665: MikeJid = escalus_users:get_jid(Config, mike), 666: 667: {AliceName, Domain, _} = get_user_data(alice, Config), 668: {BobName, Domain, _} = get_user_data(bob, Config), 669: {MikeName, Domain, _} = get_user_data(mike, Config), 670: 671: {_, 0} = add_rosteritem1(AliceName, Domain, BobName, Config), 672: {_, 0} = mongooseimctl("add_rosteritem", 673: [AliceName, Domain, MikeName, 674: Domain, "My Mike", 675: "My Group", "both"], Config), 676: 677: [Push1, Push2] = escalus:wait_for_stanzas(Alice, 2), % Check roster broadcasts 678: escalus:assert(is_roster_set, Push1), 679: escalus:assert(roster_contains, [BobJid], Push1), 680: escalus:assert(is_roster_set, Push2), 681: escalus:assert(roster_contains, [MikeJid], Push2), 682: 683: {Items1, 0} = mongooseimctl("get_roster", [AliceName, Domain], Config), 684: match_roster([{BobName, Domain, "MyBob", "MyGroup", "both"}, 685: {MikeName, Domain, "MyMike", "MyGroup", "both"}], Items1), 686: 687: escalus:send(Alice, escalus_stanza:roster_get()), 688: Roster1 = escalus:wait_for_stanza(Alice), 689: escalus:assert(is_roster_result, Roster1), 690: escalus:assert(roster_contains, [BobJid], Roster1), 691: escalus:assert(roster_contains, [MikeJid], Roster1), 692: 693: {_, 0} = mongooseimctl("delete_rosteritem", 694: [AliceName, Domain, BobName, Domain], 695: Config), 696: 697: Push3 = escalus:wait_for_stanza(Alice), 698: escalus:assert(is_roster_set, Push3), 699: escalus:assert(roster_contains, [BobJid], Push3), 700: 701: {Items2, 0} = mongooseimctl("get_roster", [AliceName, Domain], Config), 702: match_roster([{MikeName, Domain, "MyMike", "MyGroup", "both"}], Items2), 703: 704: escalus:send(Alice, escalus_stanza:roster_remove_contact(MikeJid)), % cleanup 705: escalus:wait_for_stanzas(Alice, 2, 5000) 706: end). 707: 708: presence_after_add_rosteritem(Config) -> 709: escalus:story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) -> 710: BobJid = escalus_users:get_jid(Config, bob), 711: {AliceName, Domain, _} = get_user_data(alice, Config), 712: {BobName, Domain, _} = get_user_data(bob, Config), 713: 714: {_, 0} = add_rosteritem1(AliceName, Domain, BobName, Config), 715: 716: escalus:send(Alice, escalus_stanza:presence(<<"available">>)), 717: escalus:assert(is_presence, escalus:wait_for_stanza(Bob)), 718: 719: escalus:send(Alice, escalus_stanza:roster_remove_contact(BobJid)), % cleanup 720: %% Wait for stanzas, so they would not end up in the next story 721: escalus:wait_for_stanzas(Alice, 3, 5000) 722: end). 723: 724: push_roster(Config) -> 725: escalus:story(Config, [{alice, 1}], fun(Alice) -> 726: BobJid = escalus_users:get_jid(Config, bob), 727: {AliceName, Domain, _} = get_user_data(alice, Config), 728: TemplatePath = escalus_config:get_config(roster_template, Config), 729: 730: {_, 0} = mongooseimctl("push_roster", [TemplatePath, AliceName, Domain], Config), 731: escalus:send(Alice, escalus_stanza:roster_get()), 732: Roster1 = escalus:wait_for_stanza(Alice), 733: escalus:assert(is_roster_result, Roster1), 734: escalus:assert(roster_contains, [BobJid], Roster1), 735: 736: escalus:send(Alice, escalus_stanza:roster_remove_contact(BobJid)), % cleanup 737: escalus:wait_for_stanzas(Alice, 2, 5000) 738: end). 739: 740: process_rosteritems_list_simple(Config) -> 741: escalus:story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) -> 742: %% given 743: Action = "list", 744: Subs = "any", 745: Asks = "any", 746: User = escalus_client:short_jid(Alice), 747: Contact = string:to_lower(binary_to_list(escalus_client:short_jid(Bob))), 748: {AliceName, Domain, _} = get_user_data(alice, Config), 749: {BobName, Domain, _} = get_user_data(bob, Config), 750: %% when 751: {_, 0} = add_rosteritem1(AliceName, Domain, BobName, Config), 752: _S = escalus:wait_for_stanzas(Alice, 2), 753: {R, 0} = mongooseimctl("process_rosteritems", [Action, Subs, Asks, User, Contact], Config), 754: %% then 755: {match, _} = re:run(R, ".*Matches:.*" ++ Contact ++ ".*"), 756: {_, 0} = mongooseimctl("delete_rosteritem", [AliceName, Domain, BobName, Domain], Config) 757: end). 758: 759: process_rosteritems_list_nomatch(Config) -> 760: escalus:story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) -> 761: %% given 762: Action = "list", 763: Subs = "from:both", 764: Asks = "any", 765: User = escalus_client:short_jid(Alice), 766: Contact =string:to_lower(binary_to_list(escalus_client:short_jid(Bob))), 767: {AliceName, Domain, _} = get_user_data(alice, Config), 768: {BobName, Domain, _} = get_user_data(bob, Config), 769: {_, 0} = mongooseimctl("add_rosteritem", [AliceName, Domain, BobName, 770: Domain, "MyBob", "MyGroup", "to"], Config), 771: escalus:wait_for_stanzas(Alice, 2), 772: %% when 773: {R, 0} = mongooseimctl("process_rosteritems", [Action, Subs, Asks, User, Contact], Config), 774: %% then 775: nomatch = re:run(R, ".*Matches:.*" ++ Contact ++ ".*"), 776: {_, 0} = mongooseimctl("delete_rosteritem", [AliceName, Domain, BobName, Domain], Config) 777: end). 778: 779: process_rosteritems_list_advanced1(Config) -> 780: escalus:story(Config, [{alice, 1}, {mike, 1}, {kate, 1}], fun(Alice, Mike, Kate) -> 781: %% given 782: Action = "list", 783: Subs = "from:both", 784: Asks = "any", 785: User = escalus_client:short_jid(Alice), 786: {AliceName, Domain, _} = get_user_data(alice, Config), 787: {MikeName, Domain, _} = get_user_data(mike, Config), 788: {KateName, Domain, _} = get_user_data(kate, Config), 789: ContactMike = string:to_lower(binary_to_list(escalus_client:short_jid(Mike))), 790: ContactKate= string:to_lower(binary_to_list(escalus_client:short_jid(Kate))), 791: ContactsRegexp = ContactMike ++ ":" ++ 792: string:substr(binary_to_list(KateName), 1, 2) ++ 793: ".*@.*", 794: 795: {_, 0} = add_rosteritem2(AliceName, Domain, MikeName, Domain, Config), 796: {_, 0} = mongooseimctl("add_rosteritem", [AliceName, Domain, KateName, 797: Domain, "BestFriend", "MyGroup", "both"], Config), 798: escalus:wait_for_stanzas(Alice, 4), 799: %% when 800: {R, 0} = mongooseimctl("process_rosteritems", 801: [Action, Subs, Asks, User, ContactsRegexp], 802: Config), 803: %% then 804: {match, _} = re:run(R, ".*Matches:.*" ++ ContactMike ++ ".*"), 805: {match, _} = re:run(R, ".*Matches:.*" ++ ContactKate ++ ".*"), 806: {_, 0} = mongooseimctl("delete_rosteritem", [AliceName, Domain, MikeName, Domain], Config), 807: {_, 0} = mongooseimctl("delete_rosteritem", [AliceName, Domain, KateName, Domain], Config) 808: end). 809: 810: process_rosteritems_delete_advanced(Config) -> 811: escalus:story(Config, [{alice, 1}, {mike, 1}, {kate, 1}], fun(Alice, Mike, Kate) -> 812: %% given 813: Action = "delete", 814: Subs = "from", 815: Asks = "any", 816: User = escalus_client:short_jid(Alice), 817: {AliceName, Domain, _} = get_user_data(alice, Config), 818: {MikeName, Domain, _} = get_user_data(mike, Config), 819: {KateName, Domain, _} = get_user_data(kate, Config), 820: ContactMike = string:to_lower(binary_to_list(escalus_client:short_jid(Mike))), 821: ContactKate= string:to_lower(binary_to_list(escalus_client:short_jid(Kate))), 822: ContactsRegexp = ".*" ++ string:substr(ContactMike, 3) ++ 823: ":" ++ string:substr(ContactKate, 1, 2) ++ 824: "@" ++ binary_to_list(Domain), 825: {_, 0} = mongooseimctl("add_rosteritem", [AliceName, Domain, MikeName, 826: Domain, "DearMike", "MyGroup", "from"], Config), 827: {_, 0} = mongooseimctl("add_rosteritem", [AliceName, Domain, KateName, 828: Domain, "Friend", "MyGroup", "from"], Config), 829: escalus:wait_for_stanzas(Alice, 4), 830: %% when 831: {R, 0} = mongooseimctl("process_rosteritems", 832: [Action, Subs, Asks, User, ContactsRegexp], 833: Config), 834: %% then 835: {match, _} = re:run(R, ".*Matches:.*" ++ ContactMike ++ ".*"), 836: nomatch = re:run(R, ".*Matches:.*" ++ ContactKate ++ ".*"), 837: {_, 0} = mongooseimctl("delete_rosteritem", [AliceName, Domain, MikeName, Domain], Config), 838: {_, 0} = mongooseimctl("delete_rosteritem", [AliceName, Domain, KateName, Domain], Config) 839: end). 840: 841: process_rosteritems_list_advanced2(Config) -> 842: escalus:story(Config, [{alice, 1}, {mike, 1}, {kate, 1}], fun(Alice, Mike, Kate) -> 843: %% given 844: Action = "list", 845: Subs = "any", 846: Asks = "any", 847: User = escalus_client:short_jid(Alice), 848: {AliceName, Domain, _} = get_user_data(alice, Config), 849: {MikeName, Domain, _} = get_user_data(mike, Config), 850: {KateName, Domain, _} = get_user_data(kate, Config), 851: ContactMike = string:to_lower(binary_to_list(escalus_client:short_jid(Mike))), 852: ContactKate= string:to_lower(binary_to_list(escalus_client:short_jid(Kate))), 853: ContactsRegexp = ".*e@lo.*", 854: {_, 0} = add_rosteritem2(AliceName, Domain, MikeName, Domain, Config), 855: {_, 0} = mongooseimctl("add_rosteritem", [AliceName, Domain, KateName, 856: Domain, "KateFromSchool", 857: "MyGroup", "from"], Config), 858: escalus:wait_for_stanzas(Alice, 4), 859: %% when 860: {R, 0} = mongooseimctl("process_rosteritems", 861: [Action, Subs, Asks, User, ContactsRegexp], 862: Config), 863: %% then 864: {match, _} = re:run(R, ".*Matches:.*" ++ ContactMike ++ ".*"), 865: {match, _} = re:run(R, ".*Matches:.*" ++ ContactKate ++ ".*"), 866: {_, 0} = mongooseimctl("delete_rosteritem", [AliceName, Domain, MikeName, Domain], Config), 867: {_, 0} = mongooseimctl("delete_rosteritem", [AliceName, Domain, KateName, Domain], Config) 868: end). 869: 870: process_rosteritems_delete_advanced2(Config) -> 871: escalus:story(Config, [{alice, 1}, {bob, 1}, {mike, 1}, {kate, 1}], 872: fun(Alice, Bob, Mike, Kate) -> 873: %% given 874: Action = "delete", 875: Subs = "to:from", 876: Asks = "any", 877: User = "al.c[e]@.*host:((b[o]b)|(mike))@loc.*t2", 878: {AliceName, Domain, _} = get_user_data(alice, Config), 879: {BobName, Domain, _} = get_user_data(bob, Config), 880: {MikeName, Domain, _} = get_user_data(mike, Config), 881: {KateName, Domain, _} = get_user_data(kate, Config), 882: ContactMike = string:to_lower(binary_to_list(escalus_client:short_jid(Mike))), 883: ContactKate= string:to_lower(binary_to_list(escalus_client:short_jid(Kate))), 884: ContactBob= string:to_lower(binary_to_list(escalus_client:short_jid(Bob))), 885: ContactsReg = ".ik[ea]@localho+.*:k@loc.*st:(alice)+@.*:no", 886: {_, 0} = mongooseimctl("add_rosteritem", 887: [AliceName, Domain, MikeName, 888: Domain, "DearMike", "MyGroup", "to"], 889: Config), 890: {_, 0} = mongooseimctl("add_rosteritem", 891: [AliceName, Domain, KateName, 892: Domain, "HateHerSheHasSoNiceLegs", 893: "MyGroup", "to"], Config), 894: {_, 0} = mongooseimctl("add_rosteritem", [BobName, Domain, AliceName, 895: Domain, "Girlfriend", "MyGroup", "from"], Config), 896: escalus:wait_for_stanzas(Alice, 4), 897: escalus:wait_for_stanzas(Bob, 2), 898: %% when 899: {R, 0} = mongooseimctl("process_rosteritems", 900: [Action, Subs, Asks, User, ContactsReg], 901: Config), 902: %% then 903: {match, _} = re:run(R, ".*Matches:.*" ++ ContactMike ++ ".*"), 904: nomatch = re:run(R, ".*Matches:.*" ++ ContactKate ++ ".*"), 905: nomatch = re:run(R, ".*Matches:.*" ++ ContactBob ++ ".*"), 906: {_, 0} = mongooseimctl("delete_rosteritem", [AliceName, Domain, MikeName, Domain], Config), 907: {_, 0} = mongooseimctl("delete_rosteritem", [AliceName, Domain, KateName, Domain], Config), 908: {_, 0} = mongooseimctl("delete_rosteritem", [BobName, Domain, AliceName, Domain], Config) 909: end). 910: 911: push_roster_all(Config) -> 912: escalus:story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) -> 913: TemplatePath = escalus_config:get_config(roster_template, Config), 914: 915: {_, 0} = mongooseimctl("push_roster_all", [TemplatePath], Config), 916: 917: escalus:send(Alice, escalus_stanza:roster_get()), 918: Roster1 = escalus:wait_for_stanza(Alice), 919: escalus:assert(is_roster_result, Roster1), 920: BobJid = escalus_client:short_jid(Bob), 921: escalus:assert(roster_contains, [BobJid], Roster1), 922: 923: escalus:send(Bob, escalus_stanza:roster_get()), 924: Roster2 = escalus:wait_for_stanza(Bob), 925: escalus:assert(is_roster_result, Roster2), 926: AliceJid = escalus_client:short_jid(Alice), 927: escalus:assert(roster_contains, [AliceJid], Roster2), 928: 929: escalus:send_and_wait(Alice, escalus_stanza:roster_remove_contact(bob)), % cleanup 930: escalus:send_and_wait(Bob, escalus_stanza:roster_remove_contact(alice)) % cleanup 931: end). 932: 933: push_roster_alltoall(Config) -> 934: escalus:story(Config, [{alice, 1}], fun(Alice) -> 935: BobJid = escalus_users:get_jid(Config, bob), 936: MikeJid = escalus_users:get_jid(Config, mike), 937: KateJid = escalus_users:get_jid(Config, kate), 938: {_, Domain, _} = get_user_data(alice, Config), 939: 940: {_, 0} = mongooseimctl("push_roster_alltoall", [Domain, "MyGroup"], Config), 941: 942: escalus:send(Alice, escalus_stanza:roster_get()), 943: Roster = escalus:wait_for_stanza(Alice), 944: 945: escalus:assert(is_roster_result, Roster), 946: escalus:assert(roster_contains, [BobJid], Roster), 947: escalus:assert(roster_contains, [MikeJid], Roster), 948: escalus:assert(roster_contains, [KateJid], Roster) 949: end). 950: 951: %%-------------------------------------------------------------------- 952: %% service_admin_extra_last tests 953: %%-------------------------------------------------------------------- 954: 955: set_last(Config) -> 956: escalus:story(Config, [{alice, 1}], fun(Alice) -> 957: BobJid = escalus_users:get_jid(Config, bob), 958: {AliceName, Domain, _} = get_user_data(alice, Config), 959: {BobName, Domain, _} = get_user_data(bob, Config), 960: 961: {_, 0} = add_rosteritem1(AliceName, Domain, BobName, Config), 962: {_, 0} = mongooseimctl("add_rosteritem", 963: [BobName, Domain, AliceName, 964: Domain, "MyAlice", "MyGroup", "both"], 965: Config), 966: 967: escalus:wait_for_stanza(Alice), % ignore push 968: 969: Now = os:system_time(second), 970: TS = integer_to_list(Now - 7200), 971: {_, 0} = mongooseimctl("set_last", [BobName, Domain, TS, "Status"], Config), 972: escalus:send(Alice, escalus_stanza:last_activity(BobJid)), 973: LastAct = escalus:wait_for_stanza(Alice), 974: escalus:assert(is_last_result, LastAct), 975: Seconds = list_to_integer(binary_to_list( 976: exml_query:path(LastAct, [{element, <<"query">>}, 977: {attr, <<"seconds">>}]))), 978: true = (( (Seconds > 7100) andalso (Seconds < 7300) ) orelse Seconds), 979: 980: {_, 0} = mongooseimctl("delete_rosteritem", 981: [AliceName, Domain, BobName, Domain], 982: Config), % cleanup 983: {_, 0} = mongooseimctl("delete_rosteritem", 984: [BobName, Domain, AliceName, Domain], 985: Config) 986: end). 987: 988: %%-------------------------------------------------------------------- 989: %% service_admin_extra_private tests 990: %%-------------------------------------------------------------------- 991: 992: private_rw(Config) -> 993: {AliceName, Domain, _} = get_user_data(alice, Config), 994: XmlEl1 = "<secretinfo xmlns=\"nejmspejs\">1</secretinfo>", 995: XmlEl2 = "<secretinfo xmlns=\"inny\">2</secretinfo>", 996: 997: {_, 0} = mongooseimctl("private_set", [AliceName, Domain, XmlEl1], Config), 998: {_, 0} = mongooseimctl("private_set", [AliceName, Domain, XmlEl2], Config), 999: 1000: {Result, 0} = mongooseimctl("private_get", 1001: [AliceName, Domain, "secretinfo", "nejmspejs"], 1002: Config), 1003: {ok, #xmlel{ name = <<"secretinfo">>, attrs = [{<<"xmlns">>, <<"nejmspejs">>}], 1004: children = [#xmlcdata{ content = <<"1">> }]}} = exml:parse(list_to_binary(Result)). 1005: 1006: %%-------------------------------------------------------------------- 1007: %% service_admin_extra_stanza tests 1008: %%-------------------------------------------------------------------- 1009: 1010: send_message(Config) -> 1011: escalus:story(Config, [{alice, 1}, {bob, 2}], fun(Alice, Bob1, Bob2) -> 1012: {_, 0} = mongooseimctl("send_message_chat", [escalus_client:full_jid(Alice), 1013: escalus_client:full_jid(Bob1), 1014: "Hi Bob!"], Config), 1015: Stanza1 = escalus:wait_for_stanza(Bob1), 1016: escalus:assert(is_chat_message, [<<"Hi Bob!">>], Stanza1), 1017: 1018: {_, 0} = mongooseimctl("send_message_headline", 1019: [escalus_client:full_jid(Alice), 1020: escalus_client:short_jid(Bob1), 1021: "Subj", "Hi Bob!!"], Config), 1022: Stanza2 = escalus:wait_for_stanza(Bob1), 1023: Stanza3 = escalus:wait_for_stanza(Bob2), 1024: escalus:assert(is_headline_message, [<<"Subj">>, <<"Hi Bob!!">>], Stanza2), 1025: escalus:assert(is_headline_message, [<<"Subj">>, <<"Hi Bob!!">>], Stanza3) 1026: end). 1027: 1028: send_message_wrong_jid(Config) -> 1029: escalus:story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) -> 1030: {_, Err1} = mongooseimctl("send_message_chat", ["@@#$%!!.§§£", 1031: escalus_client:full_jid(Bob), 1032: "Hello bobby!"], Config), 1033: {_, Err2} = mongooseimctl("send_message_headline", ["%%@&@&@==//\///", 1034: escalus_client:short_jid(Bob), 1035: "Subj", "Are 1036: you there?"], 1037: Config), 1038: true = Err1 =/= 0, 1039: true = Err2 =/= 0, 1040: escalus_assert:has_no_stanzas(Alice), 1041: escalus_assert:has_no_stanzas(Bob) 1042: end). 1043: 1044: send_stanza(Config) -> 1045: escalus:story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) -> 1046: Domain = escalus_client:server(Alice), 1047: Resource = escalus_client:resource(Alice), 1048: {BobName, _, _} = get_user_data(bob, Config), 1049: BobJID = <<BobName/binary, $@, Domain/binary, $/, 1050: (escalus_client:resource(Bob))/binary>>, 1051: 1052: Stanza = Stanza = create_stanza(Alice, BobJID), 1053: {_, 0} = mongooseimctl("send_stanza_c2s", 1054: [BobName, Domain, Resource, Stanza], 1055: Config), 1056: 1057: Message = escalus:wait_for_stanza(Alice), 1058: escalus:assert(is_chat_message, [<<"Hi">>], Message), 1059: escalus:assert(is_stanza_from, [Bob], Message) 1060: end). 1061: 1062: send_stanzac2s_wrong(Config) -> 1063: escalus:story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) -> 1064: Domain = escalus_client:server(Alice), 1065: Resource = escalus_client:resource(Alice), 1066: WrongBobName = "bobby_the_great", 1067: {BobName, _, _} = get_user_data(bob, Config), 1068: BobJID = <<BobName/binary, $@, Domain/binary, $/, (escalus_client:resource(Bob))/binary>>, 1069: Stanza = create_stanza(Alice, BobJID), 1070: StanzaWrong = <<"<iq type='get' id='234234'><xmlns='wrongwrong'>">>, 1071: {_, Err} = mongooseimctl("send_stanza_c2s", 1072: [WrongBobName, Domain, Resource, Stanza], 1073: Config), 1074: {_, Err2} = mongooseimctl("send_stanza_c2s", 1075: [BobName, Domain, Resource, StanzaWrong], 1076: Config), 1077: 1078: true = Err =/= 0, 1079: true = Err2 =/= 0, 1080: escalus_assert:has_no_stanzas(Alice) 1081: end). 1082: 1083: create_stanza(Name1, JID2) -> 1084: exml:to_binary(escalus_stanza:from(escalus_stanza:chat_to(Name1, "Hi"), JID2)). 1085: 1086: %%-------------------------------------------------------------------- 1087: %% service_admin_extra_stats tests 1088: %%-------------------------------------------------------------------- 1089: 1090: stats_global(Config) -> 1091: escalus:story(Config, [{alice, 1}, {bob, 1}], fun(_Alice, _Bob) -> 1092: RegisteredCount = length(escalus_config:get_config(escalus_users, Config, [])), 1093: Registered = integer_to_list(RegisteredCount) ++ "\n", 1094: 1095: {UpTime, 0} = mongooseimctl("stats", ["uptimeseconds"], Config), 1096: _ = list_to_integer(string:strip(UpTime, both, $\n)), 1097: {Registered, 0} = mongooseimctl("stats", ["registeredusers"], Config), 1098: 1099: {"2\n", 0} = mongooseimctl("stats", ["onlineusersnode"], Config), 1100: 1101: {"2\n", 0} = mongooseimctl("stats", ["onlineusers"], Config) 1102: end). 1103: 1104: stats_host(Config) -> 1105: escalus:story(Config, [{alice, 1}, {bob, 1}], fun(Alice, _Bob) -> 1106: RegisteredCount = length(escalus_config:get_config(escalus_users, Config, [])), 1107: Registered = integer_to_list(RegisteredCount) ++ "\n", 1108: 1109: PriDomain = escalus_client:server(Alice), 1110: SecDomain = ct:get_config({hosts, mim, secondary_domain}), 1111: 1112: {Registered, 0} = mongooseimctl("stats_host", 1113: ["registeredusers", PriDomain], Config), 1114: {"0\n", 0} = mongooseimctl("stats_host", ["registeredusers", SecDomain], Config), 1115: 1116: {"2\n", 0} = mongooseimctl("stats_host", ["onlineusers", PriDomain], Config), 1117: {"0\n", 0} = mongooseimctl("stats_host", ["onlineusers", SecDomain], Config) 1118: end). 1119: 1120: %%-------------------------------------------------------------------- 1121: %% mongoose_graphql tests 1122: %%-------------------------------------------------------------------- 1123: 1124: can_execute_admin_queries_with_permissions(Config) -> 1125: Query = "query { checkAuth { authStatus } }", 1126: Res = mongooseimctl("graphql", [Query], Config), 1127: ?assertMatch({_, 0}, Res), 1128: Data = element(1, Res), 1129: ?assertNotEqual(nomatch, string:find(Data, "AUTHORIZED")). 1130: 1131: can_handle_execution_error(Config) -> 1132: Query = "{}", 1133: Res = mongooseimctl("graphql", [Query], Config), 1134: ?assertMatch({_, 0}, Res), 1135: Data = element(1, Res), 1136: ?assertNotEqual(nomatch, string:find(Data, "parser_error")). 1137: 1138: graphql_wrong_arguments_number(Config) -> 1139: ExpectedFragment = "This command requires", 1140: ResNoArgs = mongooseimctl("graphql", [], Config), 1141: ?assertMatch({_, 1}, ResNoArgs), 1142: Data1 = element(1, ResNoArgs), 1143: ?assertNotEqual(nomatch, string:find(Data1, ExpectedFragment)), 1144: 1145: ResTooManyArgs = mongooseimctl("graphql", ["{}", "{}"], Config), 1146: ?assertMatch({_, 1}, ResTooManyArgs), 1147: Data2 = element(1, ResTooManyArgs), 1148: ?assertNotEqual(nomatch, string:find(Data2, ExpectedFragment)). 1149: 1150: %%----------------------------------------------------------------- 1151: %% Improve coverage 1152: %%----------------------------------------------------------------- 1153: 1154: simple_register(Config) -> 1155: %% given 1156: Domain = domain(), 1157: {Name, Password} = {<<"tyler">>, <<"durden">>}, 1158: %% when 1159: {R1, 0} = mongooseimctl("registered_users", [Domain], Config), 1160: Before = length(string:tokens(R1, "\n")), 1161: {_, 0} = mongooseimctl("register", [Domain, Password], Config), 1162: {_, 0} = mongooseimctl("register_identified", [Name, Domain, Password], Config), 1163: 1164: {R2, 0} = mongooseimctl("registered_users", [Domain], Config), 1165: After = length(string:tokens(R2, "\n")), 1166: %% then 1167: 2 = After - Before. 1168: 1169: simple_unregister(Config) -> 1170: %% given 1171: Domain = domain(), 1172: {Name, _} = {<<"tyler">>, <<"durden">>}, 1173: %% when 1174: {_, 0} = mongooseimctl("unregister", [Name, Domain], Config), 1175: {R2, 0} = mongooseimctl("registered_users", [Domain], Config), 1176: %% then 1177: nomatch = re:run(R2, ".*(" ++ binary_to_list(Name) ++ ").*"). 1178: 1179: register_twice(Config) -> 1180: %% given 1181: Domain = domain(), 1182: {Name, Password} = {<<"tyler">>, <<"durden">>}, 1183: %% when 1184: {_, 0} = mongooseimctl("register_identified", [Name, Domain, Password], Config), 1185: {R, Code} = mongooseimctl("register_identified", [Name, Domain, Password], Config), 1186: %% then 1187: {match, _} = re:run(R, ".*(already registered).*"), 1188: true = (Code =/= 0), 1189: {_, 0} = mongooseimctl("unregister", [Name, Domain], Config). 1190: 1191: backup_restore_mnesia(Config) -> 1192: %% given 1193: TableName = passwd, 1194: TableSize = rpc_call(mnesia, table_info, [TableName, size]), 1195: %% Table passwd should not be empty 1196: FileName = "backup_mnesia.bup", 1197: %% when 1198: {R, 0} = mongooseimctl("backup", [FileName], Config), 1199: nomatch = re:run(R, ".+"), 1200: rpc_call(mnesia, clear_table, [TableName]), 1201: 0 = rpc_call(mnesia, table_info, [TableName, size]), 1202: {R2, 0} = mongooseimctl("restore", [FileName], Config), 1203: %% then 1204: nomatch = re:run(R2, ".+"), 1205: TableSize = rpc_call(mnesia, table_info, [TableName, size]). 1206: 1207: restore_mnesia_wrong(Config) -> 1208: FileName = "file that doesnt exist13123.bup", 1209: {R2, _} = mongooseimctl("restore", [FileName], Config), 1210: {match, Code} = re:run(R2, ".+"), 1211: true = (Code =/= 0). 1212: 1213: dump_and_load(Config) -> 1214: FileName = "dump.bup", 1215: TableName = passwd, 1216: %% Table passwd should not be empty 1217: TableSize = rpc_call(mnesia, table_info, [TableName, size]), 1218: {DumpReturns, 0} = mongooseimctl("dump", [FileName], Config), 1219: ct:log("DumpReturns ~p", [DumpReturns]), 1220: {ok, DumpData} = rpc_call(file, consult, [FileName]), 1221: ct:log("DumpData ~p", [DumpData]), 1222: rpc_call(mnesia, clear_table, [TableName]), 1223: 0 = rpc_call(mnesia, table_info, [TableName, size]), 1224: {R, 0} = mongooseimctl("load", [FileName], Config), 1225: ct:log("LoadReturns ~p", [R]), 1226: {match, _} = re:run(R, ".+"), 1227: TableSize = rpc_call(mnesia, table_info, [TableName, size]). 1228: 1229: load_mnesia_wrong(Config) -> 1230: FileName = "file that doesnt existRHCP.bup", 1231: {R2, Code} = mongooseimctl("restore", [FileName], Config), 1232: {match, _} = re:run(R2, ".+"), 1233: true = (Code =/= 0). 1234: 1235: dump_table(Config) -> 1236: FileName = "dump.mn", 1237: TableName = passwd, 1238: %% Table passwd should not be empty 1239: TableSize = rpc_call(mnesia, table_info, [TableName, size]), 1240: {_, 0} = mongooseimctl("dump_table", [FileName, atom_to_list(TableName)], Config), 1241: rpc_call(mnesia, clear_table, [TableName]), 1242: 0 = rpc_call(mnesia, table_info, [TableName, size]), 1243: {R, 0} = mongooseimctl("load", [FileName], Config), 1244: {match, _} = re:run(R, ".+"), 1245: TableSize = rpc_call(mnesia, table_info, [TableName, size]). 1246: 1247: get_loglevel(Config) -> 1248: {R, 0} = mongooseimctl("get_loglevel", [], Config), 1249: LogLevel = rpc_call(mongoose_logs, get_global_loglevel, []), 1250: Regexp = io_lib:format("global loglevel is \(.\)\{1,2\}, which means '~p'", [LogLevel]), 1251: {match, _} = re:run(R, Regexp, [{capture, first}]). 1252: 1253: remove_old_messages_test(Config) -> 1254: escalus:story(Config, [{alice, 1}], fun(_) -> 1255: %% given 1256: JidA = nick_to_jid(alice, Config), 1257: JidB = nick_to_jid(bob, Config), 1258: JidRecordAlice = jid:from_binary(JidA), 1259: JidRecordBob = jid:from_binary(JidB), 1260: Domain = domain(), 1261: Msg1 = escalus_stanza:chat_to(<<"bob@", Domain/binary>>, 1262: "Hi, how are you? Its old message!"), 1263: Msg2 = escalus_stanza:chat_to(<<"bob@", Domain/binary>>, 1264: "Hello its new message!"), 1265: OldTimestamp = fallback_timestamp(10, os:system_time(microsecond)), 1266: OfflineOld = generate_offline_message(JidRecordAlice, JidRecordBob, Msg1, OldTimestamp), 1267: OfflineNew = generate_offline_message(JidRecordAlice, JidRecordBob, Msg2, os:system_time(microsecond)), 1268: {LUser, LServer} = jid:to_lus(JidRecordBob), 1269: HostType = host_type(), 1270: rpc_call(mod_offline_backend, write_messages, [host_type(), LUser, LServer, [OfflineOld, OfflineNew]]), 1271: %% when 1272: {_, 0} = mongooseimctl("delete_old_messages", [LServer, "1"], Config), 1273: {ok, SecondList} = rpc_call(mod_offline_backend, pop_messages, [HostType, JidRecordBob]), 1274: %% then 1275: 1 = length(SecondList) 1276: end). 1277: 1278: remove_expired_messages_test(Config) -> 1279: escalus:story(Config, [{mike, 1}], fun(_) -> 1280: %% given 1281: JidA = nick_to_jid(mike, Config), 1282: JidB = nick_to_jid(kate, Config), 1283: JidRecordMike = jid:from_binary(JidA), 1284: JidRecordKate = jid:from_binary(JidB), 1285: Domain = domain(), 1286: Msg1 = escalus_stanza:chat_to(<<"kate@", Domain/binary>>, "Rolling stones"), 1287: Msg2 = escalus_stanza:chat_to(<<"kate@", Domain/binary>>, "Arctic monkeys!"), 1288: Msg3 = escalus_stanza:chat_to(<<"kate@", Domain/binary>>, "More wine..."), 1289: Msg4 = escalus_stanza:chat_to(<<"kate@", Domain/binary>>, "kings of leon"), 1290: OldTimestamp = fallback_timestamp(10, os:system_time(microsecond)), 1291: ExpirationTime = fallback_timestamp(2, os:system_time(microsecond)), 1292: ExpirationTimeFuture= fallback_timestamp(-5, os:system_time(microsecond)), 1293: OfflineOld = generate_offline_expired_message(JidRecordMike, 1294: JidRecordKate, Msg1, 1295: OldTimestamp, 1296: ExpirationTime), 1297: OfflineNow = generate_offline_expired_message(JidRecordMike, 1298: JidRecordKate, Msg2, os:system_time(microsecond), ExpirationTime), 1299: OfflineFuture = generate_offline_expired_message(JidRecordMike, 1300: JidRecordKate, Msg3, os:system_time(microsecond), ExpirationTimeFuture), 1301: OfflineFuture2 = generate_offline_expired_message(JidRecordMike, 1302: JidRecordKate, Msg4, 1303: OldTimestamp, 1304: ExpirationTimeFuture), 1305: {LUser, LServer} = jid:to_lus(JidRecordKate), 1306: Args = [OfflineOld, OfflineNow, OfflineFuture, OfflineFuture2], 1307: HostType = host_type(), 1308: rpc_call(mod_offline_backend, write_messages, [HostType, LUser, LServer, Args]), 1309: %% when 1310: {_, 0} = mongooseimctl("delete_expired_messages", [LServer], Config), 1311: {ok, SecondList} = rpc_call(mod_offline_backend, pop_messages, [HostType, JidRecordKate]), 1312: %% then 1313: 2 = length(SecondList) 1314: end). 1315: 1316: %%----------------------------------------------------------------- 1317: %% Helpers 1318: %%----------------------------------------------------------------- 1319: 1320: 1321: nick_to_jid(UserName, Config) when is_atom(UserName) -> 1322: UserSpec = escalus_users:get_userspec(Config, UserName), 1323: escalus_utils:jid_to_lower(escalus_users:get_jid(Config, UserSpec)). 1324: 1325: generate_offline_message(From, To, Msg, TimeStamp) -> 1326: {LUser, LServer} = jid:to_lus(To), 1327: #offline_msg{us = {LUser, LServer}, timestamp = TimeStamp, expire = never, 1328: from = From, to = To, packet = Msg}. 1329: 1330: generate_offline_expired_message(From, To, Msg, TimeStamp, ExpirationTime) -> 1331: {LUser, LServer} = jid:to_lus(To), 1332: #offline_msg{us = {LUser, LServer}, timestamp = TimeStamp, 1333: expire = ExpirationTime, from = From, to = To, packet = Msg}. 1334: 1335: 1336: fallback_timestamp(HowManyDays, TS_MicroSeconds) -> 1337: HowManySeconds = HowManyDays * 86400, 1338: HowManyMicroSeconds = erlang:convert_time_unit(HowManySeconds, second, microsecond), 1339: TS_MicroSeconds - HowManyMicroSeconds. 1340: 1341: get_user_data(User, Config) when is_atom(User) -> 1342: get_user_data(escalus_users:get_options(Config, User, <<"newres">>), Config); 1343: get_user_data(User, _Config) -> 1344: {_, Password} = lists:keyfind(password, 1, User), 1345: {_, Username} = lists:keyfind(username, 1, User), 1346: {_, Domain} = lists:keyfind(server, 1, User), 1347: {Username, Domain, Password}. 1348: 1349: get_md5(AccountPass) -> 1350: lists:flatten([io_lib:format("~.16B", [X]) 1351: || X <- binary_to_list(crypto:hash(md5, AccountPass))]). 1352: get_sha(AccountPass) -> 1353: lists:flatten([io_lib:format("~.16B", [X]) 1354: || X <- binary_to_list(crypto:hash(sha, AccountPass))]). 1355: 1356: set_last(User, Domain, TStamp) -> 1357: rpc(mim(), mod_last, store_last_info, 1358: [host_type(), escalus_utils:jid_to_lower(User), Domain, TStamp, <<>>]). 1359: 1360: delete_users(_Config) -> 1361: lists:foreach(fun({User, Domain}) -> 1362: JID = mongoose_helper:make_jid(User, Domain), 1363: rpc(mim(), ejabberd_auth, remove_user, [JID]) 1364: end, get_registered_users()). 1365: 1366: %%----------------------------------------------------------------- 1367: %% Predicates 1368: %%----------------------------------------------------------------- 1369: 1370: match_user_status(Users, StatusTxt) -> 1371: Statuses = string:tokens(StatusTxt, "\n"), 1372: true = (length(Users) == length(Statuses)), 1373: match_user_status2(Users, Statuses). 1374: 1375: match_user_status2([], _) -> 1376: true; 1377: match_user_status2([User | UserR], Statuses) -> 1378: Username = binary_to_list(escalus_client:username(User)), 1379: Domain = binary_to_list(escalus_client:server(User)), 1380: Resource = binary_to_list(escalus_client:resource(User)), 1381: 1382: true = lists:any(fun(Status) -> 1383: [Username, Domain, Resource] 1384: =:= 1385: lists:sublist(string:tokens(Status, "\t"), 1, 3) 1386: end, Statuses), 1387: match_user_status2(UserR, Statuses). 1388: 1389: match_user_info(Users, UsersTxt) -> 1390: UsersInfo = string:tokens(UsersTxt, "\n"), 1391: case length(Users) == length(UsersInfo) of 1392: true -> 1393: ok; 1394: false -> 1395: ct:fail(#{what => match_user_info_failed, 1396: users => Users, user_info => UsersInfo}) 1397: end, 1398: match_user_info2(Users, UsersInfo). 1399: 1400: match_user_info2([], _) -> 1401: true; 1402: match_user_info2([User | UserR], UsersInfo) -> 1403: Username = binary_to_list(escalus_client:username(User)), 1404: Domain = binary_to_list(escalus_client:server(User)), 1405: Resource = binary_to_list(escalus_client:resource(User)), 1406: FullJID = Username ++ "@" ++ Domain ++ "/" ++ Resource, 1407: 1408: true = lists:any(fun(UserInfo) -> 1409: string:str(UserInfo, string:to_lower(FullJID)) =:= 1 1410: end, UsersInfo), 1411: match_user_info2(UserR, UsersInfo). 1412: 1413: match_roster(ItemsValid, Items) -> 1414: ItemsTokens = [ string:tokens(ItemToken, "\t") || ItemToken <- string:tokens(Items, "\n") ], 1415: 1416: true = (length(ItemsValid) == length(ItemsTokens)), 1417: true = lists:all(fun({Username, Domain, _Nick, _Group, _Sub}) -> 1418: JID = escalus_utils:jid_to_lower(<<Username/binary, "@", Domain/binary >>), 1419: lists:any(fun 1420: ([RosterJID, _Nick, _Sub, "none", _Group]) -> 1421: JID =:= escalus_utils:jid_to_lower(list_to_binary(RosterJID)); 1422: (_) -> 1423: false 1424: end, ItemsTokens) 1425: end, ItemsValid). 1426: 1427: string_to_binary(List) -> 1428: case erlang:system_info(otp_release) of 1429: [$R|_] -> 1430: list_to_binary(List); 1431: _ -> 1432: unicode:characters_to_binary(List) 1433: end. 1434: 1435: add_rosteritem1(UserName1, Domain, UserName2, Config) -> 1436: mongooseimctl("add_rosteritem", 1437: [UserName1, Domain, UserName2, 1438: Domain, "MyBob", "MyGroup", "both"], Config). 1439: 1440: add_rosteritem2(Name1, Domain1, Name2, Domain2, Config) -> 1441: mongooseimctl("add_rosteritem", 1442: [Name1, Domain1, Name2, 1443: Domain2, "DearMike", "MyGroup", "both"], Config).