1: -module(graphql_offline_SUITE).
    2: 
    3: -compile([export_all, nowarn_export_all]).
    4: 
    5: -import(distributed_helper, [require_rpc_nodes/1]).
    6: -import(domain_helper, [host_type/0, domain/0]).
    7: -import(graphql_helper, [execute_user/3, execute_auth/2, user_to_bin/1]).
    8: -import(config_parser_helper, [mod_config/2]).
    9: -import(mongooseimctl_helper, [mongooseimctl/3, rpc_call/3]).
   10: 
   11: -include_lib("common_test/include/ct.hrl").
   12: -include_lib("eunit/include/eunit.hrl").
   13: -include_lib("exml/include/exml.hrl").
   14: -include_lib("escalus/include/escalus.hrl").
   15: -include("../../include/mod_roster.hrl").
   16: 
   17: -record(offline_msg, {us, timestamp, expire, from, to, packet, permanent_fields = []}).
   18: 
   19: suite() ->
   20:     require_rpc_nodes([mim]) ++ escalus:suite().
   21: 
   22: all() ->
   23:     [{group, admin_offline},
   24:      {group, admin_offline_not_configured}].
   25: 
   26: groups() ->
   27:     [{admin_offline, [], admin_offline_handler()},
   28:      {admin_offline_not_configured, [], admin_offline_not_configured_handler()}].
   29: 
   30: admin_offline_handler() ->
   31:     [admin_delete_expired_messages_test,
   32:      admin_delete_old_messages_test,
   33:      admin_delete_expired_messages2_test,
   34:      admin_delete_old_messages2_test,
   35:      admin_delete_expired_messages_no_domain_test,
   36:      admin_delete_old_messages_no_domain_test].
   37: 
   38: admin_offline_not_configured_handler() ->
   39:     [admin_delete_expired_messages_offline_not_configured_test,
   40:      admin_delete_old_messages_offline_not_configured_test].
   41: 
   42: init_per_suite(Config) ->
   43:     Config1 = dynamic_modules:save_modules(host_type(), Config),
   44:     escalus:init_per_suite(Config1).
   45: 
   46: -spec create_config(atom()) -> [{mod_offline, gen_mod:module_opts()}].
   47: create_config(riak) ->
   48:     [{mod_offline, mod_config(mod_offline, #{backend => riak,
   49:         riak => #{bucket_type => <<"offline">>}})}];
   50: create_config(Backend) ->
   51:     [{mod_offline, mod_config(mod_offline, #{backend => Backend})}].
   52: 
   53: 
   54: end_per_suite(Config) ->
   55:     dynamic_modules:restore_modules(Config),
   56:     escalus:end_per_suite(Config).
   57: 
   58: init_per_group(admin_offline, Config) ->
   59:     HostType = host_type(),
   60:     Backend = mongoose_helper:get_backend_mnesia_rdbms_riak(HostType),
   61:     ModConfig = create_config(Backend),
   62:     dynamic_modules:ensure_modules(HostType, ModConfig),
   63:     Config1 = [{backend, Backend} | escalus:init_per_suite(Config)],
   64:     graphql_helper:init_admin_handler(Config1);
   65: init_per_group(admin_offline_not_configured, Config) ->
   66:     dynamic_modules:ensure_modules(host_type(), [{mod_offline, stopped}]),
   67:     graphql_helper:init_admin_handler(Config).
   68: 
   69: end_per_group(_, _Config) ->
   70:     escalus_fresh:clean().
   71: 
   72: init_per_testcase(CaseName, Config) ->
   73:     escalus:init_per_testcase(CaseName, Config).
   74: 
   75: end_per_testcase(CaseName, Config) ->
   76:     escalus:end_per_testcase(CaseName, Config).
   77: 
   78: % Admin test cases
   79: 
   80: admin_delete_expired_messages_test(Config) ->
   81:     Vars = #{<<"domain">> => domain()},
   82:     GraphQlRequest = admin_delete_expired_messages_mutation(Config, Vars),
   83:     Message = ok_result(<<"offline">>, <<"deleteExpiredMessages">>, GraphQlRequest),
   84:     ?assertEqual(<<"Removed 0 messages">>, Message).
   85: 
   86: admin_delete_old_messages_test(Config) ->
   87:     Vars = #{<<"domain">> => domain(), <<"days">> => 2},
   88:     GraphQlRequest = admin_delete_old_messages_mutation(Config, Vars),
   89:     Message = ok_result(<<"offline">>, <<"deleteOldMessages">>, GraphQlRequest),
   90:     ?assertEqual(<<"Removed 0 messages">>, Message).
   91: 
   92: admin_delete_expired_messages2_test(Config) ->
   93:     escalus:fresh_story_with_config(Config, [{mike, 1}, {kate, 1}], fun admin_delete_expired_messages2_test/3).
   94: 
   95: admin_delete_expired_messages2_test(Config, JidMike, JidKate) ->
   96:     generate_message(JidMike, JidKate, 10, 2),
   97:     generate_message(JidMike, JidKate, 10, 2),
   98:     Vars = #{<<"domain">> => domain()},
   99:     GraphQlRequest = admin_delete_expired_messages_mutation(Config, Vars),
  100:     Message = ok_result(<<"offline">>, <<"deleteExpiredMessages">>, GraphQlRequest),
  101:     ?assertEqual(<<"Removed 2 messages">>, Message).
  102: 
  103: admin_delete_old_messages2_test(Config) ->
  104:     escalus:fresh_story_with_config(Config, [{mike, 1}, {kate, 1}], fun admin_delete_old_messages2_test/3).
  105: 
  106: admin_delete_old_messages2_test(Config, JidMike, JidKate) ->
  107:     generate_message(JidMike, JidKate, 2, 10),
  108:     generate_message(JidMike, JidKate, 2, 10),
  109:     Vars = #{<<"domain">> => domain(), <<"days">> => 2},
  110:     GraphQlRequest = admin_delete_old_messages_mutation(Config, Vars),
  111:     Message = ok_result(<<"offline">>, <<"deleteOldMessages">>, GraphQlRequest),
  112:     ?assertEqual(<<"Removed 2 messages">>, Message).
  113: 
  114: admin_delete_expired_messages_no_domain_test(Config) ->
  115:     Vars = #{<<"domain">> => <<"AAAA">>},
  116:     GraphQlRequest = admin_delete_expired_messages_mutation(Config, Vars),
  117:     ParsedResult = error_result(<<"extensions">>, <<"code">>, GraphQlRequest),
  118:     ?assertEqual(<<"domain_not_found">>, ParsedResult).
  119: 
  120: admin_delete_old_messages_no_domain_test(Config) ->
  121:     Vars = #{<<"domain">> => <<"AAAA">>, <<"days">> => 2},
  122:     GraphQlRequest = admin_delete_old_messages_mutation(Config, Vars),
  123:     ParsedResult = error_result(<<"extensions">>, <<"code">>, GraphQlRequest),
  124:     ?assertEqual(<<"domain_not_found">>, ParsedResult).
  125: 
  126: admin_delete_expired_messages_offline_not_configured_test(Config) ->
  127:     Vars = #{<<"domain">> => domain()},
  128:     GraphQlRequest = admin_delete_expired_messages_mutation(Config, Vars),
  129:     ParsedResult = error_result(<<"extensions">>, <<"code">>, GraphQlRequest),
  130:     ?assertEqual(<<"module_not_loaded_error">>, ParsedResult).
  131: 
  132: admin_delete_old_messages_offline_not_configured_test(Config) ->
  133:     Vars = #{<<"domain">> => domain(), <<"days">> => 2},
  134:     GraphQlRequest = admin_delete_old_messages_mutation(Config, Vars),
  135:     ParsedResult = error_result(<<"extensions">>, <<"code">>, GraphQlRequest),
  136:     ?assertEqual(<<"module_not_loaded_error">>, ParsedResult).
  137: 
  138: % Helpers
  139: 
  140: admin_delete_expired_messages_mutation(Config, Vars) ->
  141:     Mutation = <<"mutation M1($domain: String!)
  142:                       {offline{deleteExpiredMessages(domain: $domain)}}">>,
  143:     admin_send_mutation(Config, Vars, Mutation).
  144: 
  145: admin_delete_old_messages_mutation(Config, Vars) ->
  146:     Mutation = <<"mutation M1($domain: String!, $days: Int!)
  147:                       {offline{deleteOldMessages(domain: $domain, days: $days)}}">>,
  148:     admin_send_mutation(Config, Vars, Mutation).
  149: 
  150: admin_send_mutation(Config, Vars, Mutation) ->
  151:     Body = #{query => Mutation, operationName => <<"M1">>, variables => Vars},
  152:     execute_auth(Body, Config).
  153: 
  154: error_result(What1, What2, {{<<"200">>, <<"OK">>}, #{<<"errors">> := [Data]}}) ->
  155:     maps:get(What2, maps:get(What1, Data)).
  156: 
  157: ok_result(What1, What2, {{<<"200">>, <<"OK">>}, #{<<"data">> := Data}}) ->
  158:     maps:get(What2, maps:get(What1, Data)).
  159: 
  160: generate_message(JidMike, JidKate, TimestampDaysAgo, TimestampExpiringDaysAgo) ->
  161:     JidRecordMike = jid:from_binary(user_to_bin(JidMike)),
  162:     JidRecordKate = jid:from_binary(user_to_bin(JidKate)),
  163:     Domain = domain(),
  164:     Msg1 = escalus_stanza:chat_to(<<"kate@", Domain/binary>>, "Rolling stones"),
  165:     OldTimestamp = fallback_timestamp(TimestampDaysAgo, os:system_time(microsecond)),
  166:     ExpirationTime = fallback_timestamp(TimestampExpiringDaysAgo, os:system_time(microsecond)),
  167:     OfflineOld = generate_offline_expired_message(JidRecordMike,
  168:                                                   JidRecordKate, Msg1,
  169:                                                   OldTimestamp,
  170:                                                   ExpirationTime),
  171:     {LUser, LServer} = jid:to_lus(JidRecordKate),
  172:     rpc_call(mod_offline_backend, write_messages, [host_type(), LUser, LServer, [OfflineOld]]).
  173: 
  174: generate_offline_expired_message(From, To, Msg, TimeStamp, ExpirationTime) ->
  175:     {LUser, LServer} = jid:to_lus(To),
  176:     #offline_msg{us = {LUser, LServer}, timestamp = TimeStamp,
  177:                  expire = ExpirationTime, from = From, to = To, packet = Msg}.
  178: 
  179: fallback_timestamp(HowManyDays, TS_MicroSeconds) ->
  180:     HowManySeconds = HowManyDays * 86400,
  181:     HowManyMicroSeconds = erlang:convert_time_unit(HowManySeconds, second, microsecond),
  182:     TS_MicroSeconds - HowManyMicroSeconds.