1: -module(graphql_last_SUITE).
    2: 
    3: -compile([export_all, nowarn_export_all]).
    4: 
    5: -import(common_helper, [unprep/1]).
    6: -import(distributed_helper, [mim/0, require_rpc_nodes/1, rpc/4]).
    7: -import(graphql_helper, [execute_command/4, execute_user_command/5, user_to_bin/1, user_to_jid/1,
    8:                          get_ok_value/2, get_err_msg/1, get_err_code/1, get_unauthorized/1,
    9:                          get_not_loaded/1, get_coercion_err_msg/1]).
   10: 
   11: -include_lib("eunit/include/eunit.hrl").
   12: 
   13: -define(assertErrMsg(Res, ContainsPart), assert_err_msg(ContainsPart, Res)).
   14: -define(assertErrCode(Res, Code), assert_err_code(Code, Res)).
   15: 
   16: -define(NONEXISTENT_JID, <<"user@user.com">>).
   17: -define(NONEXISTENT_NAME, <<"user@", (domain_helper:domain())/binary>>).
   18: -define(EMPTY_NAME_JID, <<"@", (domain_helper:domain())/binary>>).
   19: -define(DEFAULT_DT, <<"2022-04-17T12:58:30.000000Z">>).
   20: -define(INVALID_TIMESTAMP, <<"20222-04-17T12:58:30.000000Z">>).
   21: -define(NONEXISTENT_DOMAIN, <<"nonexistent">>).
   22: 
   23: suite() ->
   24:     require_rpc_nodes([mim]) ++ escalus:suite().
   25: 
   26: all() ->
   27:     [{group, user},
   28:      {group, admin_http},
   29:      {group, admin_cli},
   30:      {group, domain_admin}].
   31: 
   32: groups() ->
   33:     [{user, [], user_groups()},
   34:      {user_last, [parallel], user_tests()},
   35:      {admin_http, [], admin_groups()},
   36:      {admin_cli, [], admin_groups()},
   37:      {domain_admin, [], domain_admin_groups()},
   38:      {admin_last, [], admin_last_tests()},
   39:      {domain_admin_last, [], domain_admin_last_tests()},
   40:      {admin_old_users, [], admin_old_users_tests()},
   41:      {domain_admin_old_users, [], domain_admin_old_users_tests()},
   42:      {admin_last_not_configured, [], admin_last_not_configured()},
   43:      {admin_last_not_configured_old_users, [], admin_last_not_configured_old_users()},
   44:      {admin_last_not_configured_group, [], admin_last_not_configured_groups()},
   45:      {admin_last_configured, [], admin_last_configured()},
   46:      {user_last_not_configured, [], user_last_not_configured()}].
   47: 
   48: user_groups() ->
   49:     [{group, user_last},
   50:      {group, user_last_not_configured}].
   51: 
   52: admin_groups() ->
   53:     [{group, admin_last_configured},
   54:      {group, admin_last_not_configured_group}].
   55: 
   56: admin_last_configured() ->
   57:     [{group, admin_last},
   58:      {group, admin_old_users}].
   59: 
   60: domain_admin_groups() ->
   61:     [{group, domain_admin_last},
   62:      {group, domain_admin_old_users}].
   63: 
   64: admin_last_not_configured_groups() ->
   65:     [{group, admin_last_not_configured},
   66:      {group, admin_last_not_configured_old_users}].
   67: 
   68: user_tests() ->
   69:     [user_set_last,
   70:      user_get_last,
   71:      user_get_other_user_last].
   72: 
   73: user_last_not_configured() ->
   74:     [user_set_last_not_configured,
   75:      user_get_last_not_configured].
   76: 
   77: admin_last_tests() ->
   78:     [admin_set_last,
   79:      admin_try_set_nonexistent_user_last,
   80:      admin_try_set_last_invalid_timestamp,
   81:      admin_get_last,
   82:      admin_get_nonexistent_user_last,
   83:      admin_try_get_nonexistent_last,
   84:      admin_count_active_users,
   85:      admin_try_count_nonexistent_domain_active_users,
   86:      admin_try_count_active_users_invalid_timestamp].
   87: 
   88: admin_old_users_tests() ->
   89:     [admin_list_old_users_domain,
   90:      admin_try_list_old_users_nonexistent_domain,
   91:      admin_try_list_old_users_invalid_timestamp,
   92:      admin_list_old_users_global,
   93:      admin_remove_old_users_domain,
   94:      admin_try_remove_old_users_nonexistent_domain,
   95:      admin_try_remove_old_users_invalid_timestamp,
   96:      admin_remove_old_users_global,
   97:      admin_user_without_last_info_is_old_user,
   98:      admin_logged_user_is_not_old_user].
   99: 
  100: domain_admin_last_tests() ->
  101:     [admin_set_last,
  102:      domain_admin_set_user_last_no_permission,
  103:      admin_try_set_last_invalid_timestamp,
  104:      admin_get_last,
  105:      domain_admin_get_user_last_no_permission,
  106:      admin_try_get_nonexistent_last,
  107:      admin_count_active_users,
  108:      domain_admin_try_count_external_domain_active_users,
  109:      admin_try_count_active_users_invalid_timestamp].
  110: 
  111: domain_admin_old_users_tests() ->
  112:     [admin_list_old_users_domain,
  113:      admin_try_list_old_users_invalid_timestamp,
  114:      admin_try_remove_old_users_invalid_timestamp,
  115:      domain_admin_try_list_old_users_external_domain,
  116:      domain_admin_list_old_users_global,
  117:      domain_admin_remove_old_users_global,
  118:      domain_admin_try_remove_old_users_external_domain,
  119:      domain_admin_remove_old_users_global,
  120:      domain_admin_user_without_last_info_is_old_user,
  121:      domain_admin_logged_user_is_not_old_user].
  122: 
  123: admin_last_not_configured() ->
  124:     [admin_set_last_not_configured,
  125:      admin_get_last_not_configured,
  126:      admin_count_active_users_last_not_configured].
  127: 
  128: admin_last_not_configured_old_users() ->
  129:      [admin_remove_old_users_domain_last_not_configured,
  130:       admin_remove_old_users_global_last_not_configured,
  131:       admin_list_old_users_domain_last_not_configured,
  132:       admin_list_old_users_global_last_not_configured].
  133: 
  134: init_per_suite(Config) ->
  135:     HostType = domain_helper:host_type(),
  136:     SecHostType = domain_helper:secondary_host_type(),
  137:     Config1 = dynamic_modules:save_modules([HostType, SecHostType], Config),
  138:     Config2 = ejabberd_node_utils:init(mim(), Config1),
  139:     escalus:init_per_suite(Config2).
  140: 
  141: end_per_suite(Config) ->
  142:     dynamic_modules:restore_modules(Config),
  143:     escalus:end_per_suite(Config).
  144: 
  145: init_per_group(user, Config) ->
  146:     graphql_helper:init_user(Config);
  147: init_per_group(admin_http, Config) ->
  148:     graphql_helper:init_admin_handler(Config);
  149: init_per_group(admin_cli, Config) ->
  150:     graphql_helper:init_admin_cli(Config);
  151: init_per_group(domain_admin, Config) ->
  152:     configure_last(Config),
  153:     graphql_helper:init_domain_admin_handler(Config);
  154: init_per_group(domain_admin_last, Config) ->
  155:     Config;
  156: init_per_group(user_last, Config) ->
  157:     configure_last(Config);
  158: init_per_group(user_last_not_configured, Config) ->
  159:     stop_last(Config);
  160: init_per_group(admin_last, Config) ->
  161:     Config;
  162: init_per_group(admin_last_configured, Config) ->
  163:     configure_last(Config);
  164: init_per_group(admin_last_not_configured_group, Config) ->
  165:     stop_last(Config);
  166: init_per_group(Group, Config) when Group =:= admin_old_users;
  167:                                    Group =:= domain_admin_old_users;
  168:                                    Group =:= admin_last_not_configured_old_users ->
  169:     AuthMods = mongoose_helper:auth_modules(),
  170:     case lists:member(ejabberd_auth_ldap, AuthMods) of
  171:         true -> {skip, not_fully_supported_with_ldap};
  172:         false -> Config
  173:     end;
  174: init_per_group(admin_last_not_configured, Config) ->
  175:     Config.
  176: 
  177: configure_last(Config) ->
  178:     HostType = domain_helper:host_type(),
  179:     SecHostType = domain_helper:secondary_host_type(),
  180:     Backend = mongoose_helper:get_backend_mnesia_rdbms(HostType),
  181:     SecBackend = mongoose_helper:get_backend_mnesia_rdbms(SecHostType),
  182:     dynamic_modules:ensure_modules(HostType, required_modules(Backend)),
  183:     dynamic_modules:ensure_modules(SecHostType, required_modules(SecBackend)),
  184:     Config.
  185: 
  186: stop_last(Config) ->
  187:     HostType = domain_helper:host_type(),
  188:     SecHostType = domain_helper:secondary_host_type(),
  189:     dynamic_modules:ensure_modules(HostType, [{mod_last, stopped}]),
  190:     dynamic_modules:ensure_modules(SecHostType, [{mod_last, stopped}]),
  191:     Config.
  192: 
  193: end_per_group(GroupName, _Config) when GroupName =:= admin_http;
  194:                                        GroupName =:= admin_cli ->
  195:     graphql_helper:clean();
  196: end_per_group(user, _Config) ->
  197:     graphql_helper:clean(),
  198:     escalus_fresh:clean();
  199: end_per_group(_GroupName, _Config) ->
  200:     escalus_fresh:clean().
  201: 
  202: init_per_testcase(C, Config) when C =:= admin_remove_old_users_domain;
  203:                                   C =:= admin_remove_old_users_global;
  204:                                   C =:= admin_list_old_users_domain;
  205:                                   C =:= admin_list_old_users_global;
  206:                                   C =:= admin_user_without_last_info_is_old_user;
  207:                                   C =:= domain_admin_list_old_users_global;
  208:                                   C =:= domain_admin_remove_old_users_global;
  209:                                   C =:= domain_admin_user_without_last_info_is_old_user ->
  210:     Config1 = escalus:create_users(Config, escalus:get_users([alice, bob, alice_bis])),
  211:     escalus:init_per_testcase(C, Config1);
  212: init_per_testcase(CaseName, Config) ->
  213:     escalus:init_per_testcase(CaseName, Config).
  214: 
  215: end_per_testcase(C, Config) when C =:= admin_remove_old_users_domain;
  216:                                  C =:= admin_remove_old_users_global;
  217:                                  C =:= admin_list_old_users_domain;
  218:                                  C =:= admin_list_old_users_global;
  219:                                  C =:= admin_user_without_last_info_is_old_user;
  220:                                  C =:= domain_admin_list_old_users_global;
  221:                                  C =:= domain_admin_remove_old_users_global;
  222:                                  C =:= domain_admin_user_without_last_info_is_old_user->
  223:     escalus:delete_users(Config, escalus:get_users([alice, bob, alice_bis])),
  224:     escalus:end_per_testcase(C, Config);
  225: end_per_testcase(CaseName, Config) ->
  226:     escalus:end_per_testcase(CaseName, Config).
  227: 
  228: required_modules(Backend) ->
  229:     [{mod_last, #{backend => Backend,
  230:                   iqdisc => one_queue}}].
  231: 
  232: %% Admin test cases
  233: 
  234: admin_set_last(Config) ->
  235:     escalus:fresh_story_with_config(Config, [{alice, 1}],
  236:                                     fun admin_set_last_story/2).
  237: 
  238: admin_set_last_story(Config, Alice) ->
  239:     Status = <<"First status">>,
  240:     JID = escalus_utils:jid_to_lower(user_to_bin(Alice)),
  241:     % With timestamp provided
  242:     Res = admin_set_last(Alice, Status, ?DEFAULT_DT, Config),
  243:     #{<<"user">> := JID, <<"status">> := Status, <<"timestamp">> := ?DEFAULT_DT} =
  244:         get_ok_value(p(setLast), Res),
  245:     % Without timestamp provided
  246:     Status2 = <<"Second status">>,
  247:     Res2 = admin_set_last(Alice, Status2, null, Config),
  248:     #{<<"user">> := JID, <<"status">> := Status2, <<"timestamp">> := DateTime2} =
  249:         get_ok_value(p(setLast), Res2),
  250:     ?assert(os:system_time(second) - dt_to_unit(DateTime2, second) < 2).
  251: 
  252: admin_try_set_nonexistent_user_last(Config) ->
  253:     Res = admin_set_last(?NONEXISTENT_JID, <<"status">>, null, Config),
  254:     ?assertErrMsg(Res, <<"not exist">>),
  255:     ?assertErrCode(Res, user_does_not_exist),
  256:     Res2 = admin_set_last(?NONEXISTENT_NAME, <<"status">>, null, Config),
  257:     ?assertErrMsg(Res2, <<"not exist">>),
  258:     ?assertErrCode(Res2, user_does_not_exist),
  259:     Res3 = admin_set_last(?EMPTY_NAME_JID, <<"status">>, null, Config),
  260:     get_coercion_err_msg(Res3).
  261: 
  262: admin_try_set_last_invalid_timestamp(Config) ->
  263:     escalus:fresh_story_with_config(Config, [{alice, 1}],
  264:                                     fun admin_try_set_last_invalid_timestamp_story/2).
  265: 
  266: admin_try_set_last_invalid_timestamp_story(Config, Alice) ->
  267:     Res = admin_set_last(Alice, <<"status">>, ?INVALID_TIMESTAMP, Config),
  268:     get_coercion_err_msg(Res).
  269: 
  270: admin_get_last(Config) ->
  271:     escalus:fresh_story_with_config(Config, [{alice, 1}],
  272:                                     fun admin_get_last_story/2).
  273: 
  274: admin_get_last_story(Config, Alice) ->
  275:     Status = <<"I love ducks">>,
  276:     JID = escalus_utils:jid_to_lower(user_to_bin(Alice)),
  277:     admin_set_last(Alice, Status, ?DEFAULT_DT, Config),
  278:     Res = admin_get_last(Alice, Config),
  279:     #{<<"user">> := JID, <<"status">> := Status, <<"timestamp">> := ?DEFAULT_DT} =
  280:         get_ok_value(p(getLast), Res).
  281: 
  282: admin_get_nonexistent_user_last(Config) ->
  283:     Res = admin_get_last(?NONEXISTENT_JID, Config),
  284:     ?assertErrMsg(Res, <<"not exist">>),
  285:     ?assertErrCode(Res, user_does_not_exist),
  286:     Res2 = admin_get_last(?NONEXISTENT_NAME, Config),
  287:     ?assertErrMsg(Res2, <<"not exist">>),
  288:     ?assertErrCode(Res2, user_does_not_exist),
  289:     Res3 = admin_get_last(?EMPTY_NAME_JID, Config),
  290:     get_coercion_err_msg(Res3).
  291: 
  292: admin_try_get_nonexistent_last(Config) ->
  293:     escalus:fresh_story_with_config(Config, [{alice, 1}],
  294:                                     fun admin_try_get_nonexistent_last_story/2).
  295: 
  296: admin_try_get_nonexistent_last_story(Config, Alice) ->
  297:     Res = admin_get_last(Alice, Config),
  298:     ?assertErrMsg(Res, <<"not found">>),
  299:     ?assertErrCode(Res, last_not_found).
  300: 
  301: admin_count_active_users(Config) ->
  302:     escalus:fresh_story_with_config(Config, [{alice, 1}, {bob, 1}],
  303:                                     fun admin_count_active_users_story/3).
  304: 
  305: admin_count_active_users_story(Config, Alice, Bob) ->
  306:     Domain = domain_helper:domain(),
  307:     set_last(Alice, now_dt_with_offset(5), Config),
  308:     set_last(Bob, now_dt_with_offset(10), Config),
  309:     Res = admin_count_active_users(Domain, null, Config),
  310:     ?assertEqual(2, get_ok_value(p(countActiveUsers), Res)),
  311:     Res1 = admin_count_active_users(unprep(Domain), null, Config),
  312:     ?assertEqual(2, get_ok_value(p(countActiveUsers), Res1)),
  313:     Res2 = admin_count_active_users(Domain, now_dt_with_offset(30), Config),
  314:     ?assertEqual(0, get_ok_value(p(countActiveUsers), Res2)).
  315: 
  316: admin_try_count_nonexistent_domain_active_users(Config) ->
  317:     Res = admin_count_active_users(<<"unknown-domain.com">>, null, Config),
  318:     ?assertErrMsg(Res, <<"not found">>),
  319:     ?assertErrCode(Res, domain_not_found).
  320: 
  321: admin_try_count_active_users_invalid_timestamp(Config) ->
  322:     Domain = domain_helper:domain(),
  323:     Res = admin_count_active_users(Domain, ?INVALID_TIMESTAMP, Config),
  324:     get_coercion_err_msg(Res).
  325: 
  326: %% Admin old users test cases
  327: 
  328: admin_remove_old_users_domain(Config) ->
  329:     jids_with_config(Config, [alice, alice_bis, bob], fun admin_remove_old_users_domain_story/4).
  330: 
  331: admin_remove_old_users_domain_story(Config, Alice, AliceBis, Bob) ->
  332:     Domain = domain_helper:domain(),
  333:     ToRemoveDateTime = now_dt_with_offset(100),
  334: 
  335:     set_last(Bob, ToRemoveDateTime, Config),
  336:     set_last(AliceBis, ToRemoveDateTime, Config),
  337:     set_last(Alice, now_dt_with_offset(200), Config),
  338: 
  339:     Resp = admin_remove_old_users(Domain, now_dt_with_offset(150), Config),
  340:     [#{<<"jid">> := Bob, <<"timestamp">> := BobDateTimeRes}] = get_ok_value(p(removeOldUsers), Resp),
  341:     ?assertEqual(dt_to_unit(ToRemoveDateTime, second), dt_to_unit(BobDateTimeRes, second)),
  342:     ?assertMatch({user_does_not_exist, _}, check_account(Bob)),
  343:     ?assertMatch({ok, _}, check_account(Alice)),
  344:     ?assertMatch({ok, _}, check_account(AliceBis)).
  345: 
  346: admin_try_remove_old_users_nonexistent_domain(Config) ->
  347:     Res = admin_remove_old_users(?NONEXISTENT_DOMAIN, now_dt_with_offset(0), Config),
  348:     ?assertErrMsg(Res, <<"not found">>),
  349:     ?assertErrCode(Res, domain_not_found).
  350: 
  351: admin_try_remove_old_users_invalid_timestamp(Config) ->
  352:     Domain = domain_helper:domain(),
  353:     Res = admin_remove_old_users(Domain, ?INVALID_TIMESTAMP, Config),
  354:     get_coercion_err_msg(Res).
  355: 
  356: admin_remove_old_users_global(Config) ->
  357:     jids_with_config(Config, [alice, alice_bis, bob], fun admin_remove_old_users_global_story/4).
  358: 
  359: admin_remove_old_users_global_story(Config, Alice, AliceBis, Bob) ->
  360:     ToRemoveDateTime = now_dt_with_offset(100),
  361:     ToRemoveTimestamp = dt_to_unit(ToRemoveDateTime, second),
  362: 
  363:     set_last(Bob, ToRemoveDateTime, Config),
  364:     set_last(AliceBis, ToRemoveDateTime, Config),
  365:     set_last(Alice, now_dt_with_offset(200), Config),
  366: 
  367:     Resp = admin_remove_old_users(null, now_dt_with_offset(150), Config),
  368:     [#{<<"jid">> := AliceBis, <<"timestamp">> := AliceBisDateTime},
  369:      #{<<"jid">> := Bob, <<"timestamp">> := BobDateTime}] =
  370:         lists:sort(get_ok_value(p(removeOldUsers), Resp)),
  371:     ?assertEqual(ToRemoveTimestamp, dt_to_unit(BobDateTime, second)),
  372:     ?assertEqual(ToRemoveTimestamp, dt_to_unit(AliceBisDateTime, second)),
  373:     ?assertMatch({user_does_not_exist, _}, check_account(Bob)),
  374:     ?assertMatch({user_does_not_exist, _}, check_account(AliceBis)),
  375:     ?assertMatch({ok, _}, check_account(Alice)).
  376: 
  377: admin_list_old_users_domain(Config) ->
  378:     jids_with_config(Config, [alice, bob], fun admin_list_old_users_domain_story/3).
  379: 
  380: admin_list_old_users_domain_story(Config, Alice, Bob) ->
  381:     Domain = domain_helper:domain(),
  382:     OldDateTime = now_dt_with_offset(100),
  383: 
  384:     set_last(Bob, OldDateTime, Config),
  385:     set_last(Alice, now_dt_with_offset(200), Config),
  386: 
  387:     Res = admin_list_old_users(Domain, now_dt_with_offset(150), Config),
  388:     [#{<<"jid">> := Bob, <<"timestamp">> := BobDateTime}] = get_ok_value(p(listOldUsers), Res),
  389:     ?assertEqual(dt_to_unit(OldDateTime, second), dt_to_unit(BobDateTime, second)).
  390: 
  391: admin_try_list_old_users_nonexistent_domain(Config) ->
  392:     Res = admin_list_old_users(?NONEXISTENT_DOMAIN, now_dt_with_offset(0), Config),
  393:     ?assertErrMsg(Res, <<"not found">>),
  394:     ?assertErrCode(Res, domain_not_found).
  395: 
  396: admin_try_list_old_users_invalid_timestamp(Config) ->
  397:     Domain = domain_helper:domain(),
  398:     Res = admin_list_old_users(Domain, ?INVALID_TIMESTAMP, Config),
  399:     get_coercion_err_msg(Res).
  400: 
  401: admin_list_old_users_global(Config) ->
  402:     jids_with_config(Config, [alice, alice_bis, bob], fun admin_list_old_users_global_story/4).
  403: 
  404: admin_list_old_users_global_story(Config, Alice, AliceBis, Bob) ->
  405:     OldDateTime = now_dt_with_offset(100),
  406: 
  407:     set_last(Bob, OldDateTime, Config),
  408:     set_last(AliceBis, OldDateTime, Config),
  409:     set_last(Alice, now_dt_with_offset(200), Config),
  410: 
  411:     Res = admin_list_old_users(null, now_dt_with_offset(150), Config),
  412:     [#{<<"jid">> := AliceBis, <<"timestamp">> := AliceBisDateTime},
  413:      #{<<"jid">> := Bob, <<"timestamp">> := BobDateTime}] =
  414:         lists:sort(get_ok_value(p(listOldUsers), Res)),
  415:     ?assertEqual(dt_to_unit(OldDateTime, second), dt_to_unit(BobDateTime, second)),
  416:     ?assertEqual(dt_to_unit(OldDateTime, second), dt_to_unit(AliceBisDateTime, second)).
  417: 
  418: admin_user_without_last_info_is_old_user(Config) ->
  419:     Res = admin_list_old_users(null, now_dt_with_offset(150), Config),
  420:     OldUsers = get_ok_value(p(listOldUsers), Res),
  421:     ?assertEqual(3, length(OldUsers)),
  422:     [?assertEqual(null, TS) || #{<<"timestamp">> := TS} <- OldUsers].
  423: 
  424: admin_logged_user_is_not_old_user(Config) ->
  425:     escalus:fresh_story_with_config(Config, [{alice, 1}],
  426:                                     fun admin_logged_user_is_not_old_user_story/2).
  427: 
  428: admin_logged_user_is_not_old_user_story(Config, _Alice) ->
  429:     Res = admin_list_old_users(null, now_dt_with_offset(100), Config),
  430:     ?assertEqual([], get_ok_value(p(listOldUsers), Res)).
  431: 
  432: %% Domain admin test cases
  433: 
  434: domain_admin_set_user_last_no_permission(Config) ->
  435:     get_unauthorized(admin_set_last(?NONEXISTENT_JID, <<"status">>, null, Config)),
  436:     escalus:fresh_story(Config, [{alice_bis, 1}], fun(AliceBis) ->
  437:         BinJID = escalus_client:full_jid(AliceBis),
  438:         Resp = admin_set_last(BinJID, <<"status">>, null, Config),
  439:         get_unauthorized(Resp)
  440:     end).
  441: 
  442: domain_admin_get_user_last_no_permission(Config) ->
  443:     get_unauthorized(admin_get_last(?NONEXISTENT_JID, Config)),
  444:     escalus:fresh_story(Config, [{alice_bis, 1}], fun(AliceBis) ->
  445:         BinJID = escalus_client:full_jid(AliceBis),
  446:         Resp = admin_get_last(BinJID, Config),
  447:         get_unauthorized(Resp)
  448:     end).
  449: 
  450: domain_admin_try_count_external_domain_active_users(Config) ->
  451:     get_unauthorized(admin_count_active_users(?NONEXISTENT_DOMAIN, null, Config)),
  452:     get_unauthorized(admin_count_active_users(domain_helper:secondary_domain(), null, Config)).
  453: 
  454: %% Domain admin old users test cases
  455: 
  456: domain_admin_try_list_old_users_external_domain(Config) ->
  457:     ExternalDomain = domain_helper:secondary_domain(),
  458:     get_unauthorized(admin_list_old_users(?NONEXISTENT_DOMAIN, now_dt_with_offset(0), Config)),
  459:     get_unauthorized(admin_list_old_users(ExternalDomain, now_dt_with_offset(0), Config)).
  460: 
  461: domain_admin_try_remove_old_users_external_domain(Config) ->
  462:     ExternalDomain = domain_helper:secondary_domain(),
  463:     get_unauthorized(admin_remove_old_users(?NONEXISTENT_DOMAIN, now_dt_with_offset(0), Config)),
  464:     get_unauthorized(admin_remove_old_users(ExternalDomain, now_dt_with_offset(0), Config)).
  465: 
  466: domain_admin_list_old_users_global(Config) ->
  467:     jids_with_config(Config, [alice, alice_bis, bob],
  468:                      fun domain_admin_list_old_users_global_story/4).
  469: 
  470: domain_admin_list_old_users_global_story(Config, Alice, AliceBis, Bob) ->
  471:     OldDateTime = now_dt_with_offset(100),
  472: 
  473:     set_last(Bob, OldDateTime, Config),
  474:     set_last(AliceBis, OldDateTime, Config),
  475:     set_last(Alice, now_dt_with_offset(200), Config),
  476: 
  477:     get_unauthorized(admin_list_old_users(null, now_dt_with_offset(150), Config)).
  478: 
  479: domain_admin_remove_old_users_global(Config) ->
  480:     jids_with_config(Config, [alice, alice_bis, bob],
  481:                      fun domain_admin_remove_old_users_global_story/4).
  482: 
  483: domain_admin_remove_old_users_global_story(Config, Alice, AliceBis, Bob) ->
  484:     ToRemoveDateTime = now_dt_with_offset(100),
  485: 
  486:     set_last(Bob, ToRemoveDateTime, Config),
  487:     set_last(AliceBis, ToRemoveDateTime, Config),
  488:     set_last(Alice, now_dt_with_offset(200), Config),
  489: 
  490:     get_unauthorized(admin_remove_old_users(null, now_dt_with_offset(150), Config)).
  491: 
  492: domain_admin_user_without_last_info_is_old_user(Config) ->
  493:     get_unauthorized(admin_list_old_users(null, now_dt_with_offset(150), Config)).
  494: 
  495: domain_admin_logged_user_is_not_old_user(Config) ->
  496:     escalus:fresh_story_with_config(Config, [{alice, 1}],
  497:                                     fun domain_admin_logged_user_is_not_old_user_story/2).
  498: 
  499: domain_admin_logged_user_is_not_old_user_story(Config, _Alice) ->
  500:     get_unauthorized(admin_list_old_users(null, now_dt_with_offset(100), Config)).
  501: 
  502: %% User test cases
  503: 
  504: user_set_last(Config) ->
  505:     escalus:fresh_story_with_config(Config, [{alice, 1}],
  506:                                     fun user_set_last_story/2).
  507: 
  508: user_set_last_story(Config, Alice) ->
  509:     Status = <<"My first status">>,
  510:     JID = escalus_utils:jid_to_lower(user_to_bin(Alice)),
  511:     Res = user_set_last(Alice, Status, ?DEFAULT_DT, Config),
  512:     #{<<"user">> := JID, <<"status">> := Status, <<"timestamp">> := ?DEFAULT_DT} =
  513:         get_ok_value(p(setLast), Res),
  514:     Status2 = <<"Quack Quack">>,
  515:     Res2 = user_set_last(Alice, Status2, null, Config),
  516:     #{<<"user">> := JID, <<"status">> := Status2, <<"timestamp">> := DateTime2} =
  517:         get_ok_value(p(setLast), Res2),
  518:     ?assert(os:system_time(second) - dt_to_unit(DateTime2, second) < 2).
  519: 
  520: user_get_last(Config) ->
  521:     escalus:fresh_story_with_config(Config, [{alice, 1}],
  522:                                     fun user_get_last_story/2).
  523: user_get_last_story(Config, Alice) ->
  524:     Status = <<"I love ducks">>,
  525:     JID = escalus_utils:jid_to_lower(user_to_bin(Alice)),
  526:     user_set_last(Alice, Status, ?DEFAULT_DT, Config),
  527:     Res = user_get_last(Alice, Alice, Config),
  528:     #{<<"user">> := JID, <<"status">> := Status, <<"timestamp">> := ?DEFAULT_DT} =
  529:         get_ok_value(p(getLast), Res).
  530: 
  531: user_get_other_user_last(Config) ->
  532:     escalus:fresh_story_with_config(Config, [{alice, 1}, {bob, 1}],
  533:                                     fun user_get_other_user_last_story/3).
  534: 
  535: user_get_other_user_last_story(Config, Alice, Bob) ->
  536:     Status = <<"In good mood">>,
  537:     JID = escalus_utils:jid_to_lower(user_to_bin(Bob)),
  538:     user_set_last(Bob, Status, ?DEFAULT_DT, Config),
  539:     Res = user_get_last(Alice, Bob, Config),
  540:     #{<<"user">> := JID, <<"status">> := Status, <<"timestamp">> := ?DEFAULT_DT} =
  541:         get_ok_value(p(getLast), Res).
  542: 
  543: 
  544: admin_set_last_not_configured(Config) ->
  545:     escalus:fresh_story_with_config(Config, [{alice, 1}],
  546:                                     fun admin_set_last_not_configured_story/2).
  547: 
  548: admin_set_last_not_configured_story(Config, Alice) ->
  549:     Status = <<"First status">>,
  550:     Res = admin_set_last(Alice, Status, ?DEFAULT_DT, Config),
  551:     get_not_loaded(Res).
  552: 
  553: admin_get_last_not_configured(Config) ->
  554:     escalus:fresh_story_with_config(Config, [{alice, 1}],
  555:                                     fun admin_get_last_not_configured_story/2).
  556: 
  557: admin_get_last_not_configured_story(Config, Alice) ->
  558:     Res = admin_get_last(Alice, Config),
  559:     get_not_loaded(Res).
  560: 
  561: admin_count_active_users_last_not_configured(Config) ->
  562:     Domain = domain_helper:domain(),
  563:     get_not_loaded(admin_count_active_users(Domain, null, Config)),
  564:     get_not_loaded(admin_count_active_users(unprep(Domain), null, Config)).
  565: 
  566: admin_remove_old_users_domain_last_not_configured(Config) ->
  567:     Domain = domain_helper:domain(),
  568:     get_not_loaded(admin_remove_old_users(Domain, now_dt_with_offset(150), Config)),
  569:     get_not_loaded(admin_remove_old_users(unprep(Domain), now_dt_with_offset(150), Config)).
  570: 
  571: admin_remove_old_users_global_last_not_configured(Config) ->
  572:     Res = admin_remove_old_users(null, now_dt_with_offset(150), Config),
  573:     get_ok_value([], Res).
  574: 
  575: admin_list_old_users_domain_last_not_configured(Config) ->
  576:     Domain = domain_helper:domain(),
  577:     get_not_loaded(admin_list_old_users(Domain, now_dt_with_offset(150), Config)),
  578:     get_not_loaded(admin_list_old_users(unprep(Domain), now_dt_with_offset(150), Config)).
  579: 
  580: admin_list_old_users_global_last_not_configured(Config) ->
  581:     Res = admin_list_old_users(null, now_dt_with_offset(150), Config),
  582:     get_ok_value([], Res).
  583: 
  584: user_set_last_not_configured(Config) ->
  585:     escalus:fresh_story_with_config(Config, [{alice, 1}],
  586:                                     fun user_set_last_not_configured_story/2).
  587: 
  588: user_set_last_not_configured_story(Config, Alice) ->
  589:     Status = <<"My first status">>,
  590:     Res = user_set_last(Alice, Status, ?DEFAULT_DT, Config),
  591:     get_not_loaded(Res).
  592: 
  593: user_get_last_not_configured(Config) ->
  594:     escalus:fresh_story_with_config(Config, [{alice, 1}],
  595:                                     fun user_get_last_not_configured_story/2).
  596: user_get_last_not_configured_story(Config, Alice) ->
  597:     Res = user_get_last(Alice, Alice, Config),
  598:     get_not_loaded(Res).
  599: 
  600: %% Helpers
  601: 
  602: jids_with_config(Config, Users, Fun) ->
  603:     Args = [escalus_utils:jid_to_lower(escalus_users:get_jid(Config, User)) || User <- Users],
  604:     apply(Fun, [Config | Args]).
  605: 
  606: set_last(UserJID, DateTime, Config) ->
  607:     admin_set_last(UserJID, <<>>, DateTime, Config).
  608: 
  609: check_account(User) ->
  610:     {Username, LServer} = jid:to_lus(user_to_jid(User)),
  611:     rpc(mim(), mongoose_account_api, check_account, [mongoose_helper:make_jid(Username, LServer)]).
  612: 
  613: assert_err_msg(Contains, Res) ->
  614:     ?assertNotEqual(nomatch, binary:match(get_err_msg(Res), Contains)).
  615: 
  616: assert_err_code(Code, Res) ->
  617:     ?assertEqual(atom_to_binary(Code), get_err_code(Res)).
  618: 
  619: p(Cmd) when is_atom(Cmd) ->
  620:     [data, last, Cmd];
  621: p(Path) when is_list(Path) ->
  622:     [data, last] ++ Path.
  623: 
  624: now_dt_with_offset(SecondsOffset) ->
  625:     Seconds = erlang:system_time(second) + SecondsOffset,
  626:     list_to_binary(calendar:system_time_to_rfc3339(Seconds, [{unit, second}, {offset, "Z"}])).
  627: 
  628: dt_to_unit(ISODateTime, Unit) ->
  629:     calendar:rfc3339_to_system_time(binary_to_list(ISODateTime), [{unit, Unit}]).
  630: 
  631: %% Commands
  632: 
  633: admin_set_last(User, Status, DateTime, Config) ->
  634:     Vars = #{user => user_to_bin(User), timestamp => DateTime, status => Status},
  635:     execute_command(<<"last">>, <<"setLast">>, Vars, Config).
  636: 
  637: admin_get_last(User, Config) ->
  638:     Vars = #{user => user_to_bin(User)},
  639:     execute_command(<<"last">>, <<"getLast">>, Vars, Config).
  640: 
  641: admin_count_active_users(Domain, Timestamp, Config) ->
  642:     Vars = #{domain => Domain, timestamp => Timestamp},
  643:     execute_command(<<"last">>, <<"countActiveUsers">>, Vars, Config).
  644: 
  645: admin_remove_old_users(Domain, Timestamp, Config) ->
  646:     Vars = #{domain => Domain, timestamp => Timestamp},
  647:     execute_command(<<"last">>, <<"removeOldUsers">>, Vars, Config).
  648: 
  649: admin_list_old_users(Domain, Timestamp, Config) ->
  650:     Vars = #{domain => Domain, timestamp => Timestamp},
  651:     execute_command(<<"last">>, <<"listOldUsers">>, Vars, Config).
  652: 
  653: user_set_last(User, Status, DateTime, Config) ->
  654:     Vars = #{timestamp => DateTime, status => Status},
  655:     execute_user_command(<<"last">>, <<"setLast">>, User, Vars, Config).
  656: 
  657: user_get_last(User, QueriedUser, Config) ->
  658:     Vars = #{user => user_to_bin(QueriedUser)},
  659:     execute_user_command(<<"last">>, <<"getLast">>, User, Vars, Config).