1: -module(graphql_offline_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]).
    7: -import(domain_helper, [host_type/0, domain/0]).
    8: -import(graphql_helper, [execute_command/4, get_ok_value/2, get_err_code/1, user_to_bin/1,
    9:                          get_unauthorized/1, get_not_loaded/1, get_coercion_err_msg/1]).
   10: -import(config_parser_helper, [mod_config_with_auto_backend/1]).
   11: -import(mongooseimctl_helper, [mongooseimctl/3, rpc_call/3]).
   12: 
   13: -include_lib("eunit/include/eunit.hrl").
   14: 
   15: -record(offline_msg, {us, timestamp, expire, from, to, packet, permanent_fields = []}).
   16: 
   17: suite() ->
   18:     require_rpc_nodes([mim]) ++ escalus:suite().
   19: 
   20: all() ->
   21:     [{group, admin_http},
   22:      {group, admin_cli},
   23:      {group, domain_admin}].
   24: 
   25: groups() ->
   26:     [{admin_http, [], admin_groups()},
   27:      {admin_cli, [], admin_groups()},
   28:      {domain_admin, [], domain_admin_groups()},
   29:      {admin_offline, [], admin_offline_tests()},
   30:      {admin_offline_not_configured, [], admin_offline_not_configured_tests()},
   31:      {domain_admin_offline, [], domain_admin_offline_tests()},
   32:      {domain_admin_offline_not_configured, [], domain_admin_offline_not_configured_tests()}].
   33: 
   34: admin_groups() ->
   35:     [{group, admin_offline},
   36:      {group, admin_offline_not_configured}].
   37: 
   38: domain_admin_groups() ->
   39:     [{group, domain_admin_offline},
   40:      {group, domain_admin_offline_not_configured}].
   41: 
   42: admin_offline_tests() ->
   43:     [admin_delete_expired_messages_test,
   44:      admin_delete_old_messages_test,
   45:      admin_delete_expired_messages2_test,
   46:      admin_delete_old_messages2_test,
   47:      admin_delete_old_messages_invalid_days,
   48:      admin_delete_expired_messages_no_domain_test,
   49:      admin_delete_old_messages_no_domain_test].
   50: 
   51: admin_offline_not_configured_tests() ->
   52:     [admin_delete_expired_messages_offline_not_configured_test,
   53:      admin_delete_old_messages_offline_not_configured_test].
   54: 
   55: domain_admin_offline_tests() ->
   56:     [admin_delete_expired_messages_test,
   57:      admin_delete_old_messages_test,
   58:      admin_delete_expired_messages2_test,
   59:      admin_delete_old_messages2_test,
   60:      admin_delete_old_messages_invalid_days,
   61:      domain_admin_delete_expired_messages_no_permission_test,
   62:      domain_admin_delete_old_messages_no_permission_test].
   63: 
   64: domain_admin_offline_not_configured_tests() ->
   65:     [admin_delete_expired_messages_offline_not_configured_test,
   66:      admin_delete_old_messages_offline_not_configured_test].
   67: 
   68: init_per_suite(Config) ->
   69:     Config1 = dynamic_modules:save_modules(host_type(), Config),
   70:     Config2 = ejabberd_node_utils:init(mim(), Config1),
   71:     escalus:init_per_suite(Config2).
   72: 
   73: -spec create_config() -> [{mod_offline, gen_mod:module_opts()}].
   74: create_config() ->
   75:     [{mod_offline, mod_config_with_auto_backend(mod_offline)}].
   76: 
   77: end_per_suite(Config) ->
   78:     dynamic_modules:restore_modules(Config),
   79:     escalus:end_per_suite(Config).
   80: 
   81: init_per_group(admin_http, Config) ->
   82:     graphql_helper:init_admin_handler(Config);
   83: init_per_group(admin_cli, Config) ->
   84:     graphql_helper:init_admin_cli(Config);
   85: init_per_group(domain_admin, Config) ->
   86:     graphql_helper:init_domain_admin_handler(Config);
   87: init_per_group(GroupName, Config) when GroupName =:= admin_offline;
   88:                                        GroupName =:= domain_admin_offline ->
   89:     HostType = host_type(),
   90:     dynamic_modules:ensure_modules(HostType, create_config()),
   91:     escalus:init_per_suite(Config);
   92: init_per_group(admin_offline_not_configured, Config) ->
   93:     dynamic_modules:ensure_modules(host_type(), [{mod_offline, stopped}]),
   94:     Config;
   95: init_per_group(domain_admin_offline_not_configured, Config) ->
   96:     dynamic_modules:ensure_modules(host_type(), [{mod_offline, stopped}]),
   97:     Config.
   98: 
   99: end_per_group(GroupName, _Config) when GroupName =:= admin_http;
  100:                                        GroupName =:= admin_cli;
  101:                                        GroupName =:= domain_admin ->
  102:     graphql_helper:clean();
  103: end_per_group(_, _Config) ->
  104:     ok.
  105: 
  106: init_per_testcase(CaseName, Config) ->
  107:     escalus:init_per_testcase(CaseName, Config).
  108: 
  109: end_per_testcase(CaseName, Config) ->
  110:     escalus:end_per_testcase(CaseName, Config),
  111:     escalus_fresh:clean().
  112: 
  113: % Admin test cases
  114: 
  115: admin_delete_expired_messages_test(Config) ->
  116:     Result = delete_expired_messages(domain(), Config),
  117:     ParsedResult = get_ok_value([data, offline, deleteExpiredMessages], Result),
  118:     ?assertEqual(<<"Removed 0 messages">>, ParsedResult).
  119: 
  120: admin_delete_old_messages_test(Config) ->
  121:     Result = delete_old_messages(domain(), 2, Config),
  122:     ParsedResult = get_ok_value([data, offline, deleteOldMessages], Result),
  123:     ?assertEqual(<<"Removed 0 messages">>, ParsedResult).
  124: 
  125: admin_delete_expired_messages2_test(Config) ->
  126:     escalus:fresh_story_with_config(Config, [{mike, 1}, {kate, 1}],
  127:                                     fun admin_delete_expired_messages2_test/3).
  128: 
  129: admin_delete_expired_messages2_test(Config, JidMike, JidKate) ->
  130:     admin_delete_expired_messages2(Config, JidMike, JidKate, domain()),
  131:     admin_delete_expired_messages2(Config, JidMike, JidKate, unprep(domain())).
  132: 
  133: admin_delete_expired_messages2(Config, JidMike, JidKate, Domain) ->
  134:     generate_message(JidMike, JidKate, 2, 1),
  135:     generate_message(JidMike, JidKate, 5, -1), % not expired yet
  136:     Result = delete_expired_messages(Domain, Config),
  137:     ParsedResult = get_ok_value([data, offline, deleteExpiredMessages], Result),
  138:     ?assertEqual(<<"Removed 1 messages">>, ParsedResult).
  139: 
  140: admin_delete_old_messages2_test(Config) ->
  141:     escalus:fresh_story_with_config(Config, [{mike, 1}, {kate, 1}],
  142:                                     fun admin_delete_old_messages2_test/3).
  143: 
  144: admin_delete_old_messages2_test(Config, JidMike, JidKate) ->
  145:     admin_delete_old_messages2(Config, JidMike, JidKate, domain()),
  146:     admin_delete_old_messages2(Config, JidMike, JidKate, unprep(domain())).
  147: 
  148: admin_delete_old_messages_invalid_days(Config) ->
  149:     Result = delete_old_messages(domain(), -1, Config),
  150:     ParsedResult = get_coercion_err_msg(Result),
  151:     ?assertMatch({_, _}, binary:match(ParsedResult, <<"Value is not a positive integer">>)),
  152:     Result2 = delete_old_messages(domain(), 0, Config),
  153:     ParsedResult2 = get_coercion_err_msg(Result2),
  154:     ?assertMatch({_, _}, binary:match(ParsedResult2, <<"Value is not a positive integer">>)).
  155: 
  156: admin_delete_old_messages2(Config, JidMike, JidKate, Domain) ->
  157:     generate_message(JidMike, JidKate, 2, 1), % not old enough
  158:     generate_message(JidMike, JidKate, 5, -1),
  159:     generate_message(JidMike, JidKate, 7, 5),
  160:     Result = delete_old_messages(Domain, 3, Config),
  161:     ParsedResult = get_ok_value([data, offline, deleteOldMessages], Result),
  162:     ?assertEqual(<<"Removed 2 messages">>, ParsedResult).
  163: 
  164: admin_delete_expired_messages_no_domain_test(Config) ->
  165:     Result = delete_expired_messages(<<"AAAA">>, Config),
  166:     ?assertEqual(<<"domain_not_found">>, get_err_code(Result)).
  167: 
  168: admin_delete_old_messages_no_domain_test(Config) ->
  169:     Result = delete_old_messages(<<"AAAA">>, 2, Config),
  170:     ?assertEqual(<<"domain_not_found">>, get_err_code(Result)).
  171: 
  172: admin_delete_expired_messages_offline_not_configured_test(Config) ->
  173:     get_not_loaded(delete_expired_messages(domain(), Config)),
  174:     get_not_loaded(delete_expired_messages(unprep(domain()), Config)).
  175: 
  176: admin_delete_old_messages_offline_not_configured_test(Config) ->
  177:     get_not_loaded(delete_old_messages(domain(), 2, Config)),
  178:     get_not_loaded(delete_old_messages(unprep(domain()), 2, Config)).
  179: 
  180: %% Domain admin test cases
  181: 
  182: domain_admin_delete_expired_messages_no_permission_test(Config) ->
  183:     get_unauthorized(delete_expired_messages(<<"AAAA">>, Config)),
  184:     get_unauthorized(delete_expired_messages(domain_helper:secondary_domain(), Config)).
  185: 
  186: domain_admin_delete_old_messages_no_permission_test(Config) ->
  187:     get_unauthorized(delete_old_messages(<<"AAAA">>, 2, Config)),
  188:     get_unauthorized(delete_old_messages(domain_helper:secondary_domain(), 2, Config)).
  189: 
  190: %% Commands
  191: 
  192: delete_expired_messages(Domain, Config) ->
  193:     Vars = #{<<"domain">> => Domain},
  194:     execute_command(<<"offline">>, <<"deleteExpiredMessages">>, Vars, Config).
  195: 
  196: delete_old_messages(Domain, Days, Config) ->
  197:     Vars = #{<<"domain">> => Domain, <<"days">> => Days},
  198:     execute_command(<<"offline">>, <<"deleteOldMessages">>, Vars, Config).
  199: 
  200: %% Helpers
  201: 
  202: generate_message(JidMike, JidKate, TimestampDaysAgo, TimestampExpiringDaysAgo) ->
  203:     JidRecordMike = jid:from_binary(user_to_bin(JidMike)),
  204:     JidRecordKate = jid:from_binary(user_to_bin(JidKate)),
  205:     Domain = domain(),
  206:     Msg1 = escalus_stanza:chat_to(<<"kate@", Domain/binary>>, "Rolling stones"),
  207:     OldTimestamp = fallback_timestamp(TimestampDaysAgo, os:system_time(microsecond)),
  208:     ExpirationTime = fallback_timestamp(TimestampExpiringDaysAgo, os:system_time(microsecond)),
  209:     OfflineOld = generate_offline_expired_message(JidRecordMike,
  210:                                                   JidRecordKate, Msg1,
  211:                                                   OldTimestamp,
  212:                                                   ExpirationTime),
  213:     {LUser, LServer} = jid:to_lus(JidRecordKate),
  214:     rpc_call(mod_offline_backend, write_messages, [host_type(), LUser, LServer, [OfflineOld]]).
  215: 
  216: generate_offline_expired_message(From, To, Msg, TimeStamp, ExpirationTime) ->
  217:     {LUser, LServer} = jid:to_lus(To),
  218:     #offline_msg{us = {LUser, LServer}, timestamp = TimeStamp,
  219:                  expire = ExpirationTime, from = From, to = To, packet = Msg}.
  220: 
  221: fallback_timestamp(HowManyDays, TS_MicroSeconds) ->
  222:     HowManySeconds = HowManyDays * 86400,
  223:     HowManyMicroSeconds = erlang:convert_time_unit(HowManySeconds, second, microsecond),
  224:     TS_MicroSeconds - HowManyMicroSeconds.