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: -import(config_parser_helper, [mod_config_with_auto_backend/2]).
   24: 
   25: %%--------------------------------------------------------------------
   26: %% Suite configuration
   27: %%--------------------------------------------------------------------
   28: 
   29: all() ->
   30:     [{group, valid_queries},
   31:      {group, invalid_queries}].
   32: 
   33: groups() ->
   34:     [{valid_queries, [sequence], valid_test_cases()},
   35:      {invalid_queries, invalid_test_cases()}].
   36: 
   37: valid_test_cases() -> [online_user_query,
   38:                        last_online_user,
   39:                        last_offline_user,
   40:                        last_server].
   41: 
   42: invalid_test_cases() -> [user_not_subscribed_receives_error].
   43: 
   44: suite() ->
   45:     escalus:suite().
   46: 
   47: init_per_suite(Config0) ->
   48:     HostType = domain_helper:host_type(),
   49:     Config1 = dynamic_modules:save_modules(HostType, Config0),
   50:     dynamic_modules:ensure_modules(HostType, required_modules()),
   51:     escalus:init_per_suite(Config1).
   52: 
   53: end_per_suite(Config) ->
   54:     dynamic_modules:restore_modules(Config),
   55:     escalus:end_per_suite(Config).
   56: 
   57: init_per_group(valid_queries, Config0) ->
   58:     Config1 = escalus_fresh:create_users(Config0, [{alice, 1}, {bob, 1}]),
   59:     Config2 = escalus:make_everyone_friends(Config1),
   60:     %% This check ensures that there are no registered sessions.
   61:     %% But in mongoose_c2s we first unset session,
   62:     %% then broadcast presence unavailable.
   63:     %% This check uses ejabberd_sm to get information about sessions.
   64:     escalus_ejabberd:wait_for_session_count(Config2, 0),
   65:     %% Kick "friendly" users
   66:     %% kick_everyone uses mongoose_c2s_sup to information about client processes.
   67:     mongoose_helper:kick_everyone(),
   68:     Config2;
   69: init_per_group(invalid_queries, Config) ->
   70:     Config.
   71: 
   72: end_per_group(_GroupName, _Config) ->
   73:     escalus_fresh:clean().
   74: 
   75: init_per_testcase(CaseName, Config) ->
   76:     escalus:init_per_testcase(CaseName, Config).
   77: 
   78: end_per_testcase(CaseName, Config) ->
   79:     mongoose_helper:kick_everyone(),
   80:     escalus:end_per_testcase(CaseName, Config).
   81: 
   82: %%--------------------------------------------------------------------
   83: %% Last tests
   84: %%--------------------------------------------------------------------
   85: online_user_query(Config) ->
   86:     %% Alice and Bob are friends
   87:     escalus:story(Config, [{alice, 1}, {bob, 1}],
   88:                   fun(Alice, Bob) ->
   89:                           %% Alice asks about Bob's last activity
   90:                           BobJid = escalus_utils:get_jid(Bob),
   91:                           escalus_client:send(Alice, escalus_stanza:last_activity(BobJid)),
   92:                           %% Bob gets IQ and answers
   93:                           BobGetsIQ = escalus_client:wait_for_stanza(Bob),
   94:                           escalus_client:send(Bob, answer_last_activity(BobGetsIQ)),
   95:                           Stanza = escalus_client:wait_for_stanza(Alice),
   96:                           escalus:assert(is_last_result, Stanza),
   97:                           0 = get_last_activity(Stanza)
   98:                   end).
   99: 
  100: last_online_user(Config) ->
  101:     %% Alice and Bob are friends
  102:     escalus:story(Config, [{alice, 1}, {bob, 1}],
  103:                   fun(Alice, Bob) ->
  104:                           %% Alice asks about Bob's last activity
  105:                           BobShortJID = escalus_client:short_jid(Bob),
  106:                           escalus_client:send(Alice, escalus_stanza:last_activity(BobShortJID)),
  107: 
  108:                           %% server replies on Bob's behalf
  109:                           Stanza = escalus_client:wait_for_stanza(Alice),
  110:                           escalus:assert(is_last_result, Stanza),
  111:                           0 = get_last_activity(Stanza)
  112:                   end).
  113: 
  114: last_offline_user(Config) ->
  115:     %% Alice and Bob are friends
  116:     escalus:story(Config, [{alice, 1}],
  117:                   fun(Alice) ->
  118:                           %% Bob logs in
  119:                           {ok, Bob} = escalus_client:start_for(Config, bob, <<"bob">>),
  120: 
  121:                           %% Bob logs out with a status
  122:                           Status = escalus_stanza:tags([{<<"status">>, <<"I am a banana!">>}]),
  123:                           Presence = escalus_stanza:presence(<<"unavailable">>, Status),
  124:                           escalus_client:send(Bob, Presence),
  125:                           escalus_client:stop(Config, Bob),
  126:                           timer:sleep(1024), % more than a second
  127: 
  128:                           %% Alice asks for Bob's last availability
  129:                           BobShortJID = escalus_client:short_jid(Bob),
  130:                           escalus_client:send(Alice, escalus_stanza:last_activity(BobShortJID)),
  131: 
  132:                           %% Alice receives Bob's status and last online time > 0
  133:                           Stanza = escalus_client:wait_for_stanza(Alice),
  134:                           escalus:assert(is_last_result, Stanza),
  135:                           true = (1 =< get_last_activity(Stanza)),
  136:                           <<"I am a banana!">> = get_last_status(Stanza)
  137:                   end).
  138: 
  139: last_server(Config) ->
  140:     %% This story can be fresh_story
  141:     escalus:fresh_story(Config, [{alice, 1}],
  142:                   fun(Alice) ->
  143:                           %% Alice asks for server's uptime
  144:                           Server = escalus_users:get_server(Config, alice),
  145:                           escalus_client:send(Alice, escalus_stanza:last_activity(Server)),
  146: 
  147:                           %% Server replies with the uptime > 0
  148:                           Stanza = escalus_client:wait_for_stanza(Alice),
  149:                           escalus:assert(is_last_result, Stanza),
  150:                           true = (get_last_activity(Stanza) > 0)
  151:                   end).
  152: 
  153: user_not_subscribed_receives_error(Config) ->
  154:     escalus:fresh_story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) ->
  155:         %% Alice asks about Bob's last activity
  156:         BobShortJID = escalus_client:short_jid(Bob),
  157:         escalus_client:send(Alice, escalus_stanza:last_activity(BobShortJID)),
  158: 
  159:         %% server replies with an error, since there is no subscription
  160:         Error = escalus_client:wait_for_stanza(Alice),
  161:         escalus:assert(is_error, [<<"auth">>, <<"forbidden">>], Error),
  162: 
  163:         %% Alice asks Bob directly for last activity
  164:         BobFullJID = escalus_client:full_jid(Bob),
  165:         escalus_client:send(Alice, escalus_stanza:last_activity(BobFullJID)),
  166: 
  167:         Error1 = escalus_client:wait_for_stanza(Alice),
  168:         escalus:assert(is_error, [<<"auth">>, <<"forbidden">>], Error1),
  169:         ok
  170:     end).
  171: 
  172: 
  173: %%-----------------------------------------------------------------
  174: %% Helpers
  175: %%-----------------------------------------------------------------
  176: get_last_activity(Stanza) ->
  177:     S = exml_query:path(Stanza, [{element, <<"query">>}, {attr, <<"seconds">>}]),
  178:     list_to_integer(binary_to_list(S)).
  179: 
  180: get_last_status(Stanza) ->
  181:     exml_query:path(Stanza, [{element, <<"query">>}, cdata]).
  182: 
  183: answer_last_activity(IQ = #xmlel{name = <<"iq">>}) ->
  184:     From = exml_query:attr(IQ, <<"from">>),
  185:     To = exml_query:attr(IQ, <<"to">>),
  186:     Id = exml_query:attr(IQ, <<"id">>),
  187:     #xmlel{name = <<"iq">>,
  188:            attrs = [{<<"from">>, To}, {<<"to">>, From}, {<<"id">>, Id}, {<<"type">>, <<"result">>}],
  189:            children = [#xmlel{name = <<"query">>,
  190:                               attrs = [{<<"xmlns">>, ?NS_LAST_ACTIVITY},
  191:                                        {<<"seconds">>, <<"0">>}]}
  192:                       ]}.
  193: 
  194: required_modules() ->
  195:     [{mod_last, mod_config_with_auto_backend(mod_last, #{iqdisc => one_queue})}].