1: -module(xep_0352_csi_SUITE).
    2: 
    3: -include_lib("escalus/include/escalus.hrl").
    4: 
    5: -compile([export_all, nowarn_export_all]).
    6: 
    7: -import(domain_helper, [host_type/0]).
    8: -import(config_parser_helper, [default_mod_config/1, mod_config/2]).
    9: 
   10: -define(CSI_BUFFER_MAX, 10).
   11: 
   12: all() ->
   13:     [{group, basic}].
   14: 
   15: 
   16: groups() ->
   17:     [{basic, [parallel], all_tests()}].
   18: 
   19: all_tests() ->
   20:     [
   21:      server_announces_csi,
   22:      alice_is_inactive_and_no_stanza_arrived,
   23:      inactive_twice_does_not_reset_buffer,
   24:      alice_gets_msgs_after_activate,
   25:      alice_gets_msgs_after_activate_in_order,
   26:      alice_gets_message_after_buffer_overflow,
   27:      alice_gets_buffered_messages_after_reconnection_with_sm,
   28:      alice_gets_buffered_messages_after_stream_resumption,
   29:      bob_gets_msgs_from_inactive_alice,
   30:      alice_is_inactive_but_sends_sm_req_and_recives_ack,
   31:      invalid_csi_request_returns_error
   32:     ].
   33: 
   34: suite() ->
   35:     escalus:suite().
   36: 
   37: init_per_suite(Config) ->
   38:     NewConfig = dynamic_modules:save_modules(host_type(), Config),
   39:     Backend = mongoose_helper:mnesia_or_rdbms_backend(),
   40:     dynamic_modules:ensure_modules(
   41:       host_type(), [{mod_offline, mod_config(mod_offline, #{backend => Backend})},
   42:                     {mod_csi, mod_config(mod_csi, #{buffer_max => ?CSI_BUFFER_MAX})}]),
   43:     [{escalus_user_db, {module, escalus_ejabberd}} | escalus:init_per_suite(NewConfig)].
   44: 
   45: end_per_suite(Config) ->
   46:     escalus_fresh:clean(),
   47:     dynamic_modules:restore_modules(Config),
   48:     escalus:end_per_suite(Config).
   49: 
   50: init_per_group(_, Config) ->
   51:     escalus_users:update_userspec(Config, alice, stream_management, true).
   52: 
   53: end_per_group(_Group, Config) ->
   54:     Config.
   55: 
   56: init_per_testcase(CaseName, Config) ->
   57:     escalus:init_per_testcase(CaseName, Config).
   58: 
   59: end_per_testcase(CaseName, Config) ->
   60:     escalus:end_per_testcase(CaseName, Config).
   61: 
   62: server_announces_csi(Config) ->
   63:     NewConfig = escalus_fresh:create_users(Config, [{alice, 1}]),
   64:     Spec = escalus_users:get_userspec(NewConfig, alice),
   65:     Steps = [start_stream, stream_features, maybe_use_ssl, authenticate, bind, session],
   66:     {ok, _Client, Features} = escalus_connection:start(Spec, Steps),
   67:     true = proplists:get_value(client_state_indication, Features).
   68: 
   69: invalid_csi_request_returns_error(Config) ->
   70:     escalus:fresh_story(Config, [{alice, 1}], fun(Alice) ->
   71:         escalus:send(Alice, csi_helper:csi_stanza(<<"invalid">>)),
   72:         Stanza = escalus:wait_for_stanza(Alice),
   73:         escalus:assert(is_error, [<<"modify">>, <<"bad-request">>], Stanza)
   74:     end).
   75: 
   76: alice_is_inactive_and_no_stanza_arrived(Config) ->
   77:     escalus:fresh_story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) ->
   78:         csi_helper:given_client_is_inactive_and_no_messages_arrive(Alice),
   79:         csi_helper:given_messages_are_sent(Alice, Bob, 1),
   80:         csi_helper:then_client_does_not_receive_any_message(Alice)
   81:     end).
   82: 
   83: alice_gets_msgs_after_activate(Config) ->
   84:     alice_gets_msgs_after_activate(Config, 1).
   85: 
   86: alice_gets_msgs_after_activate_in_order(Config) ->
   87:     alice_gets_msgs_after_activate(Config, 3).
   88: 
   89: alice_gets_msgs_after_activate(Config, N) ->
   90:     escalus:fresh_story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) ->
   91:         csi_helper:given_client_is_inactive_and_no_messages_arrive(Alice),
   92:         Msgs = csi_helper:given_messages_are_sent(Alice, Bob, N),
   93:         csi_helper:given_client_is_active(Alice),
   94:         csi_helper:then_client_receives_message(Alice, Msgs)
   95:     end).
   96: 
   97: inactive_twice_does_not_reset_buffer(Config) ->
   98:     escalus:fresh_story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) ->
   99:         csi_helper:given_client_is_inactive_and_no_messages_arrive(Alice),
  100:         Msgs = csi_helper:given_messages_are_sent(Alice, Bob, 2),
  101:         csi_helper:given_client_is_inactive_and_no_messages_arrive(Alice),
  102:         csi_helper:given_client_is_active(Alice),
  103:         csi_helper:then_client_receives_message(Alice, Msgs)
  104:     end).
  105: 
  106: alice_gets_buffered_messages_after_reconnection_with_sm(Config) ->
  107:     NewConfig = escalus_fresh:create_users(Config, [{alice, 1}, {bob, 1}]),
  108:     AliceSpec = escalus_users:get_userspec(NewConfig, alice),
  109:     BobSpec = escalus_users:get_userspec(NewConfig, bob),
  110:     {ok, Alice0 = #client{props = AliceProps}, _} = escalus_connection:start(AliceSpec),
  111:     JID = make_jid_from_spec(AliceProps),
  112:     Alice = Alice0#client{jid = JID},
  113:     {ok, Bob, _} = escalus_connection:start(BobSpec),
  114: 
  115:     csi_helper:given_client_is_inactive_and_no_messages_arrive(Alice),
  116: 
  117:     MsgsToAlice = csi_helper:given_messages_are_sent(Alice, Bob, 5),
  118: 
  119:     %% then Alice disconnects
  120: 
  121:     escalus_connection:kill(Alice),
  122: 
  123:     ConnSteps = [start_stream, stream_features, authenticate, bind, session],
  124:     {ok, Alice2, _} = escalus_connection:start(AliceSpec, ConnSteps),
  125: 
  126:     csi_helper:then_client_receives_message(Alice2, MsgsToAlice),
  127:     escalus_connection:stop(Alice2),
  128:     escalus_connection:stop(Bob).
  129: 
  130: alice_gets_buffered_messages_after_stream_resumption(Config) ->
  131:     ConnSteps = [start_stream, stream_features, authenticate, bind, session, stream_resumption],
  132:     NewConfig = escalus_fresh:create_users(Config, [{alice, 1}, {bob, 1}]),
  133:     AliceSpec = escalus_users:get_userspec(NewConfig, alice),
  134:     BobSpec = escalus_users:get_userspec(NewConfig, bob),
  135:     {ok, Alice0 = #client{props = AliceProps}, _} = escalus_connection:start(AliceSpec, ConnSteps),
  136:     JID = make_jid_from_spec(AliceProps),
  137:     Alice = Alice0#client{jid = JID},
  138: 
  139:     escalus_connection:send(Alice, escalus_stanza:presence(<<"available">>)),
  140:     escalus:wait_for_stanza(Alice),
  141:     {ok, Bob, _} = escalus_connection:start(BobSpec),
  142: 
  143:     csi_helper:given_client_is_inactive_and_no_messages_arrive(Alice),
  144:     MsgsToAlice = csi_helper:given_messages_are_sent(Alice, Bob, 5),
  145:     csi_helper:then_client_does_not_receive_any_message(Alice),
  146: 
  147:     %% then Alice loses connection and resumes it
  148:     escalus_connection:kill(Alice),
  149:     SMID = proplists:get_value(smid, AliceProps),
  150:     ResumeSession = [start_stream, stream_features, authenticate, mk_resume_stream(SMID, 1)],
  151:     {ok, Alice2, _} = escalus_connection:start(AliceSpec, ResumeSession),
  152: 
  153:     csi_helper:then_client_receives_message(Alice2, MsgsToAlice),
  154:     escalus_connection:stop(Alice2),
  155:     escalus_connection:stop(Bob).
  156: 
  157: make_jid_from_spec(AliceProps) ->
  158:     AliceUsername = proplists:get_value(username, AliceProps),
  159:     AliceServer = proplists:get_value(server, AliceProps),
  160:     <<AliceUsername/binary, "@", AliceServer/binary>>.
  161: 
  162: mk_resume_stream(SMID, PrevH) ->
  163:     fun (Conn = #client{props = Props}, Features) ->
  164:             escalus_connection:send(Conn, escalus_stanza:resume(SMID, PrevH)),
  165:             Resumed = escalus_connection:get_stanza(Conn, get_resumed),
  166:             true = escalus_pred:is_sm_resumed(SMID, Resumed),
  167:             {Conn#client{props = [{smid, SMID} | Props]}, Features}
  168:     end.
  169: 
  170: alice_gets_message_after_buffer_overflow(Config) ->
  171:     escalus:fresh_story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) ->
  172:         csi_helper:given_client_is_inactive_and_no_messages_arrive(Alice),
  173:         Msgs = csi_helper:given_messages_are_sent(Alice, Bob, ?CSI_BUFFER_MAX + 5),
  174:         {Flushed, Awaiting} = lists:split(?CSI_BUFFER_MAX+1, Msgs),
  175:         csi_helper:then_client_receives_message(Alice, Flushed),
  176:         %% and no other stanza
  177:         csi_helper:then_client_does_not_receive_any_message(Alice),
  178:         %% Alice activates
  179:         csi_helper:given_client_is_active(Alice),
  180:         %% ands gets remaining stanzas
  181:         csi_helper:then_client_receives_message(Alice, Awaiting)
  182:     end).
  183: 
  184: bob_gets_msgs_from_inactive_alice(Config) ->
  185:     escalus:fresh_story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) ->
  186:         given_client_is_inactive_but_sends_messages(Alice, Bob, 1),
  187:         escalus:assert(is_chat_message, escalus:wait_for_stanza(Bob))
  188:     end).
  189: 
  190: alice_is_inactive_but_sends_sm_req_and_recives_ack(Config) ->
  191:     escalus:fresh_story(Config, [{alice,1}], fun(Alice) ->
  192:         csi_helper:given_client_is_inactive_and_no_messages_arrive(Alice),
  193:         escalus:send(Alice, escalus_stanza:sm_request()),
  194:         escalus:assert(is_sm_ack, escalus:wait_for_stanza(Alice))
  195: 
  196:     end).
  197: 
  198: given_client_is_inactive_but_sends_messages(Alice, Bob, N) ->
  199:     %%Given
  200:     csi_helper:given_client_is_inactive_and_no_messages_arrive(Alice),
  201:     MsgsToAlice = csi_helper:given_messages_are_sent(Alice, Bob, N),
  202:     MsgsToBob = csi_helper:gen_msgs(<<"Hi, Bob">>, N),
  203:     csi_helper:send_msgs(Alice, Bob, MsgsToBob),
  204:     timer:sleep(1),
  205:     {MsgsToAlice, MsgsToBob}.