1: %%==============================================================================
    2: %% Copyright 2011 Erlang Solutions Ltd.
    3: %%
    4: %% Licensed under the Apache License, Version 2.0 (the "License");
    5: %% you may not use this file except in compliance with the License.
    6: %% You may obtain a copy of the License at
    7: %%
    8: %% http://www.apache.org/licenses/LICENSE-2.0
    9: %%
   10: %% Unless required by applicable law or agreed to in writing, software
   11: %% distributed under the License is distributed on an "AS IS" BASIS,
   12: %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   13: %% See the License for the specific language governing permissions and
   14: %% limitations under the License.
   15: %%==============================================================================
   16: -module(last_SUITE).
   17: -compile([export_all, nowarn_export_all]).
   18: 
   19: -include_lib("escalus/include/escalus.hrl").
   20: -include_lib("escalus/include/escalus_xmlns.hrl").
   21: -include_lib("exml/include/exml.hrl").
   22: 
   23: %%--------------------------------------------------------------------
   24: %% Suite configuration
   25: %%--------------------------------------------------------------------
   26: 
   27: all() ->
   28:     [{group, valid_queries},
   29:      {group, invalid_queries}].
   30: 
   31: groups() ->
   32:     [{valid_queries, [sequence], valid_test_cases()},
   33:      {invalid_queries, invalid_test_cases()}].
   34: 
   35: valid_test_cases() -> [online_user_query,
   36:                        last_online_user,
   37:                        last_offline_user,
   38:                        last_server].
   39: 
   40: invalid_test_cases() -> [user_not_subscribed_receives_error].
   41: 
   42: suite() ->
   43:     escalus:suite().
   44: 
   45: init_per_suite(Config0) ->
   46:     HostType = domain_helper:host_type(),
   47:     Config1 = dynamic_modules:save_modules(HostType, Config0),
   48:     Backend = mongoose_helper:get_backend_mnesia_rdbms(HostType),
   49:     dynamic_modules:ensure_modules(HostType, required_modules(Backend)),
   50:     escalus:init_per_suite([{backend, Backend} | Config1]).
   51: 
   52: end_per_suite(Config) ->
   53:     dynamic_modules:restore_modules(Config),
   54:     escalus:end_per_suite(Config).
   55: 
   56: init_per_group(valid_queries, Config0) ->
   57:     Config1 = escalus_fresh:create_users(Config0, [{alice, 1}, {bob, 1}]),
   58:     Config2 = escalus:make_everyone_friends(Config1),
   59:     %% This check ensures that there are no registered sessions.
   60:     %% But in mongoose_c2s we first unset session,
   61:     %% then broadcast presence unavailable.
   62:     %% This check uses ejabberd_sm to get information about sessions.
   63:     escalus_ejabberd:wait_for_session_count(Config2, 0),
   64:     %% Kick "friendly" users
   65:     %% kick_everyone uses mongoose_c2s_sup to information about client processes.
   66:     mongoose_helper:kick_everyone(),
   67:     Config2;
   68: init_per_group(invalid_queries, Config) ->
   69:     Config.
   70: 
   71: end_per_group(_GroupName, _Config) ->
   72:     escalus_fresh:clean().
   73: 
   74: init_per_testcase(CaseName, Config) ->
   75:     escalus:init_per_testcase(CaseName, Config).
   76: 
   77: end_per_testcase(CaseName, Config) ->
   78:     mongoose_helper:kick_everyone(),
   79:     escalus:end_per_testcase(CaseName, Config).
   80: 
   81: %%--------------------------------------------------------------------
   82: %% Last tests
   83: %%--------------------------------------------------------------------
   84: online_user_query(Config) ->
   85:     %% Alice and Bob are friends
   86:     escalus:story(Config, [{alice, 1}, {bob, 1}],
   87:                   fun(Alice, Bob) ->
   88:                           %% Alice asks about Bob's last activity
   89:                           BobJid = escalus_utils:get_jid(Bob),
   90:                           escalus_client:send(Alice, escalus_stanza:last_activity(BobJid)),
   91:                           %% Bob gets IQ and answers
   92:                           BobGetsIQ = escalus_client:wait_for_stanza(Bob),
   93:                           escalus_client:send(Bob, answer_last_activity(BobGetsIQ)),
   94:                           Stanza = escalus_client:wait_for_stanza(Alice),
   95:                           escalus:assert(is_last_result, Stanza),
   96:                           0 = get_last_activity(Stanza)
   97:                   end).
   98: 
   99: last_online_user(Config) ->
  100:     %% Alice and Bob are friends
  101:     escalus:story(Config, [{alice, 1}, {bob, 1}],
  102:                   fun(Alice, Bob) ->
  103:                           %% Alice asks about Bob's last activity
  104:                           BobShortJID = escalus_client:short_jid(Bob),
  105:                           escalus_client:send(Alice, escalus_stanza:last_activity(BobShortJID)),
  106: 
  107:                           %% server replies on Bob's behalf
  108:                           Stanza = escalus_client:wait_for_stanza(Alice),
  109:                           escalus:assert(is_last_result, Stanza),
  110:                           0 = get_last_activity(Stanza)
  111:                   end).
  112: 
  113: last_offline_user(Config) ->
  114:     %% Alice and Bob are friends
  115:     escalus:story(Config, [{alice, 1}],
  116:                   fun(Alice) ->
  117:                           %% Bob logs in
  118:                           {ok, Bob} = escalus_client:start_for(Config, bob, <<"bob">>),
  119: 
  120:                           %% Bob logs out with a status
  121:                           Status = escalus_stanza:tags([{<<"status">>, <<"I am a banana!">>}]),
  122:                           Presence = escalus_stanza:presence(<<"unavailable">>, Status),
  123:                           escalus_client:send(Bob, Presence),
  124:                           escalus_client:stop(Config, Bob),
  125:                           timer:sleep(1024), % more than a second
  126: 
  127:                           %% Alice asks for Bob's last availability
  128:                           BobShortJID = escalus_client:short_jid(Bob),
  129:                           escalus_client:send(Alice, escalus_stanza:last_activity(BobShortJID)),
  130: 
  131:                           %% Alice receives Bob's status and last online time > 0
  132:                           Stanza = escalus_client:wait_for_stanza(Alice),
  133:                           escalus:assert(is_last_result, Stanza),
  134:                           true = (1 =< get_last_activity(Stanza)),
  135:                           <<"I am a banana!">> = get_last_status(Stanza)
  136:                   end).
  137: 
  138: last_server(Config) ->
  139:     %% This story can be fresh_story
  140:     escalus:fresh_story(Config, [{alice, 1}],
  141:                   fun(Alice) ->
  142:                           %% Alice asks for server's uptime
  143:                           Server = escalus_users:get_server(Config, alice),
  144:                           escalus_client:send(Alice, escalus_stanza:last_activity(Server)),
  145: 
  146:                           %% Server replies with the uptime > 0
  147:                           Stanza = escalus_client:wait_for_stanza(Alice),
  148:                           escalus:assert(is_last_result, Stanza),
  149:                           true = (get_last_activity(Stanza) > 0)
  150:                   end).
  151: 
  152: user_not_subscribed_receives_error(Config) ->
  153:     escalus:fresh_story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) ->
  154:         %% Alice asks about Bob's last activity
  155:         BobShortJID = escalus_client:short_jid(Bob),
  156:         escalus_client:send(Alice, escalus_stanza:last_activity(BobShortJID)),
  157: 
  158:         %% server replies with an error, since there is no subscription
  159:         Error = escalus_client:wait_for_stanza(Alice),
  160:         escalus:assert(is_error, [<<"auth">>, <<"forbidden">>], Error),
  161: 
  162:         %% Alice asks Bob directly for last activity
  163:         BobFullJID = escalus_client:full_jid(Bob),
  164:         escalus_client:send(Alice, escalus_stanza:last_activity(BobFullJID)),
  165: 
  166:         Error1 = escalus_client:wait_for_stanza(Alice),
  167:         escalus:assert(is_error, [<<"auth">>, <<"forbidden">>], Error1),
  168:         ok
  169:     end).
  170: 
  171: 
  172: %%-----------------------------------------------------------------
  173: %% Helpers
  174: %%-----------------------------------------------------------------
  175: get_last_activity(Stanza) ->
  176:     S = exml_query:path(Stanza, [{element, <<"query">>}, {attr, <<"seconds">>}]),
  177:     list_to_integer(binary_to_list(S)).
  178: 
  179: get_last_status(Stanza) ->
  180:     exml_query:path(Stanza, [{element, <<"query">>}, cdata]).
  181: 
  182: answer_last_activity(IQ = #xmlel{name = <<"iq">>}) ->
  183:     From = exml_query:attr(IQ, <<"from">>),
  184:     To = exml_query:attr(IQ, <<"to">>),
  185:     Id = exml_query:attr(IQ, <<"id">>),
  186:     #xmlel{name = <<"iq">>,
  187:            attrs = [{<<"from">>, To}, {<<"to">>, From}, {<<"id">>, Id}, {<<"type">>, <<"result">>}],
  188:            children = [#xmlel{name = <<"query">>,
  189:                               attrs = [{<<"xmlns">>, ?NS_LAST_ACTIVITY},
  190:                                        {<<"seconds">>, <<"0">>}]}
  191:                       ]}.
  192: 
  193: required_modules(Backend) ->
  194:     [{mod_last, #{backend => Backend,
  195:                   iqdisc => one_queue}}].