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/2]).
   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(atom()) -> [{mod_offline, gen_mod:module_opts()}].
   74: create_config(Backend) ->
   75:     [{mod_offline, mod_config(mod_offline, #{backend => Backend})}].
   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:     Backend = mongoose_helper:get_backend_mnesia_rdbms(HostType),
   91:     ModConfig = create_config(Backend),
   92:     dynamic_modules:ensure_modules(HostType, ModConfig),
   93:     [{backend, Backend} | escalus:init_per_suite(Config)];
   94: init_per_group(admin_offline_not_configured, Config) ->
   95:     dynamic_modules:ensure_modules(host_type(), [{mod_offline, stopped}]),
   96:     Config;
   97: init_per_group(domain_admin_offline_not_configured, Config) ->
   98:     dynamic_modules:ensure_modules(host_type(), [{mod_offline, stopped}]),
   99:     Config.
  100: 
  101: end_per_group(GroupName, _Config) when GroupName =:= admin_http;
  102:                                        GroupName =:= admin_cli;
  103:                                        GroupName =:= domain_admin ->
  104:     graphql_helper:clean();
  105: end_per_group(_, _Config) ->
  106:     ok.
  107: 
  108: init_per_testcase(CaseName, Config) ->
  109:     escalus:init_per_testcase(CaseName, Config).
  110: 
  111: end_per_testcase(CaseName, Config) ->
  112:     escalus:end_per_testcase(CaseName, Config),
  113:     escalus_fresh:clean().
  114: 
  115: % Admin test cases
  116: 
  117: admin_delete_expired_messages_test(Config) ->
  118:     Result = delete_expired_messages(domain(), Config),
  119:     ParsedResult = get_ok_value([data, offline, deleteExpiredMessages], Result),
  120:     ?assertEqual(<<"Removed 0 messages">>, ParsedResult).
  121: 
  122: admin_delete_old_messages_test(Config) ->
  123:     Result = delete_old_messages(domain(), 2, Config),
  124:     ParsedResult = get_ok_value([data, offline, deleteOldMessages], Result),
  125:     ?assertEqual(<<"Removed 0 messages">>, ParsedResult).
  126: 
  127: admin_delete_expired_messages2_test(Config) ->
  128:     escalus:fresh_story_with_config(Config, [{mike, 1}, {kate, 1}],
  129:                                     fun admin_delete_expired_messages2_test/3).
  130: 
  131: admin_delete_expired_messages2_test(Config, JidMike, JidKate) ->
  132:     admin_delete_expired_messages2(Config, JidMike, JidKate, domain()),
  133:     admin_delete_expired_messages2(Config, JidMike, JidKate, unprep(domain())).
  134: 
  135: admin_delete_expired_messages2(Config, JidMike, JidKate, Domain) ->
  136:     generate_message(JidMike, JidKate, 2, 1),
  137:     generate_message(JidMike, JidKate, 5, -1), % not expired yet
  138:     Result = delete_expired_messages(Domain, Config),
  139:     ParsedResult = get_ok_value([data, offline, deleteExpiredMessages], Result),
  140:     ?assertEqual(<<"Removed 1 messages">>, ParsedResult).
  141: 
  142: admin_delete_old_messages2_test(Config) ->
  143:     escalus:fresh_story_with_config(Config, [{mike, 1}, {kate, 1}],
  144:                                     fun admin_delete_old_messages2_test/3).
  145: 
  146: admin_delete_old_messages2_test(Config, JidMike, JidKate) ->
  147:     admin_delete_old_messages2(Config, JidMike, JidKate, domain()),
  148:     admin_delete_old_messages2(Config, JidMike, JidKate, unprep(domain())).
  149: 
  150: admin_delete_old_messages_invalid_days(Config) ->
  151:     Result = delete_old_messages(domain(), -1, Config),
  152:     ParsedResult = get_coercion_err_msg(Result),
  153:     ?assertMatch({_, _}, binary:match(ParsedResult, <<"Value is not a positive integer">>)),
  154:     Result2 = delete_old_messages(domain(), 0, Config),
  155:     ParsedResult2 = get_coercion_err_msg(Result2),
  156:     ?assertMatch({_, _}, binary:match(ParsedResult2, <<"Value is not a positive integer">>)).
  157: 
  158: admin_delete_old_messages2(Config, JidMike, JidKate, Domain) ->
  159:     generate_message(JidMike, JidKate, 2, 1), % not old enough
  160:     generate_message(JidMike, JidKate, 5, -1),
  161:     generate_message(JidMike, JidKate, 7, 5),
  162:     Result = delete_old_messages(Domain, 3, Config),
  163:     ParsedResult = get_ok_value([data, offline, deleteOldMessages], Result),
  164:     ?assertEqual(<<"Removed 2 messages">>, ParsedResult).
  165: 
  166: admin_delete_expired_messages_no_domain_test(Config) ->
  167:     Result = delete_expired_messages(<<"AAAA">>, Config),
  168:     ?assertEqual(<<"domain_not_found">>, get_err_code(Result)).
  169: 
  170: admin_delete_old_messages_no_domain_test(Config) ->
  171:     Result = delete_old_messages(<<"AAAA">>, 2, Config),
  172:     ?assertEqual(<<"domain_not_found">>, get_err_code(Result)).
  173: 
  174: admin_delete_expired_messages_offline_not_configured_test(Config) ->
  175:     get_not_loaded(delete_expired_messages(domain(), Config)),
  176:     get_not_loaded(delete_expired_messages(unprep(domain()), Config)).
  177: 
  178: admin_delete_old_messages_offline_not_configured_test(Config) ->
  179:     get_not_loaded(delete_old_messages(domain(), 2, Config)),
  180:     get_not_loaded(delete_old_messages(unprep(domain()), 2, Config)).
  181: 
  182: %% Domain admin test cases
  183: 
  184: domain_admin_delete_expired_messages_no_permission_test(Config) ->
  185:     get_unauthorized(delete_expired_messages(<<"AAAA">>, Config)),
  186:     get_unauthorized(delete_expired_messages(domain_helper:secondary_domain(), Config)).
  187: 
  188: domain_admin_delete_old_messages_no_permission_test(Config) ->
  189:     get_unauthorized(delete_old_messages(<<"AAAA">>, 2, Config)),
  190:     get_unauthorized(delete_old_messages(domain_helper:secondary_domain(), 2, Config)).
  191: 
  192: %% Commands
  193: 
  194: delete_expired_messages(Domain, Config) ->
  195:     Vars = #{<<"domain">> => Domain},
  196:     execute_command(<<"offline">>, <<"deleteExpiredMessages">>, Vars, Config).
  197: 
  198: delete_old_messages(Domain, Days, Config) ->
  199:     Vars = #{<<"domain">> => Domain, <<"days">> => Days},
  200:     execute_command(<<"offline">>, <<"deleteOldMessages">>, Vars, Config).
  201: 
  202: %% Helpers
  203: 
  204: generate_message(JidMike, JidKate, TimestampDaysAgo, TimestampExpiringDaysAgo) ->
  205:     JidRecordMike = jid:from_binary(user_to_bin(JidMike)),
  206:     JidRecordKate = jid:from_binary(user_to_bin(JidKate)),
  207:     Domain = domain(),
  208:     Msg1 = escalus_stanza:chat_to(<<"kate@", Domain/binary>>, "Rolling stones"),
  209:     OldTimestamp = fallback_timestamp(TimestampDaysAgo, os:system_time(microsecond)),
  210:     ExpirationTime = fallback_timestamp(TimestampExpiringDaysAgo, os:system_time(microsecond)),
  211:     OfflineOld = generate_offline_expired_message(JidRecordMike,
  212:                                                   JidRecordKate, Msg1,
  213:                                                   OldTimestamp,
  214:                                                   ExpirationTime),
  215:     {LUser, LServer} = jid:to_lus(JidRecordKate),
  216:     rpc_call(mod_offline_backend, write_messages, [host_type(), LUser, LServer, [OfflineOld]]).
  217: 
  218: generate_offline_expired_message(From, To, Msg, TimeStamp, ExpirationTime) ->
  219:     {LUser, LServer} = jid:to_lus(To),
  220:     #offline_msg{us = {LUser, LServer}, timestamp = TimeStamp,
  221:                  expire = ExpirationTime, from = From, to = To, packet = Msg}.
  222: 
  223: fallback_timestamp(HowManyDays, TS_MicroSeconds) ->
  224:     HowManySeconds = HowManyDays * 86400,
  225:     HowManyMicroSeconds = erlang:convert_time_unit(HowManySeconds, second, microsecond),
  226:     TS_MicroSeconds - HowManyMicroSeconds.