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