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