1: %%==============================================================================
    2: %% Copyright 2014 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: 
   17: -module(mod_global_distrib_SUITE).
   18: -compile([export_all, nowarn_export_all]).
   19: -author('piotr.nosek@erlang-solutions.com').
   20: 
   21: -include_lib("exml/include/exml.hrl").
   22: -include("mongoose.hrl").
   23: -include("jlib.hrl").
   24: 
   25: -import(config_parser_helper, [mod_config/2, config/2]).
   26: 
   27: %%--------------------------------------------------------------------
   28: %% Suite configuration
   29: %%--------------------------------------------------------------------
   30: 
   31: all() ->
   32:     [{group, hook_handlers}].
   33: 
   34: groups() ->
   35:     [
   36:      {hook_handlers, [], hook_handlers_tests()}
   37:     ].
   38: 
   39: hook_handlers_tests() ->
   40:     [
   41:         missing_struct_in_message_from_user,
   42:         missing_struct_in_message_from_component
   43:     ].
   44: 
   45: suite() ->
   46:     [].
   47: 
   48: %%--------------------------------------------------------------------
   49: %% Init & teardown
   50: %%--------------------------------------------------------------------
   51: 
   52: init_per_suite(Config) ->
   53:     {ok, _} = application:ensure_all_started(jid),
   54:     {ok, _} = application:ensure_all_started(cache_tab),
   55:     Config.
   56: 
   57: end_per_suite(Config) ->
   58:     Config.
   59: 
   60: init_per_group(_GroupName, Config) ->
   61:     Config.
   62: 
   63: end_per_group(_GroupName, Config) ->
   64:     Config.
   65: 
   66: init_per_testcase(_CaseName, Config) ->
   67:     set_meck(),
   68:     [mongoose_config:set_opt(Key, Value) || {Key, Value} <- opts()],
   69:     mongoose_domain_api:init(),
   70:     mim_ct_sup:start_link(ejabberd_sup),
   71:     gen_hook:start_link(),
   72:     mongoose_modules:start(),
   73:     Config.
   74: 
   75: end_per_testcase(_CaseName, Config) ->
   76:     mongoose_modules:stop(),
   77:     mongoose_domain_api:stop(),
   78:     [mongoose_config:unset_opt(Key) || {Key, _Value} <- opts()],
   79:     unset_meck(),
   80:     Config.
   81: 
   82: opts() ->
   83:     [{hosts, hosts()},
   84:      {host_types, []},
   85:      {all_metrics_are_global, false} |
   86:      [{{modules, HostType}, modules(HostType)} || HostType <- hosts()]].
   87: 
   88: hosts() ->
   89:     [global_host(), local_host()].
   90: 
   91: modules(HostType) ->
   92:     gen_mod_deps:resolve_deps(HostType, #{mod_global_distrib => module_opts()}).
   93: 
   94: module_opts() ->
   95:     mod_config(mod_global_distrib, #{global_host => global_host(),
   96:                                      local_host => local_host(),
   97:                                      connections => connection_opts()}).
   98: 
   99: connection_opts() ->
  100:     config([modules, mod_global_distrib, connections],
  101:            #{endpoints => [],
  102:              resolved_endpoints => [],
  103:              advertised_endpoints => []}).
  104: 
  105: %%--------------------------------------------------------------------
  106: %% Hook handlers tests
  107: %%--------------------------------------------------------------------
  108: 
  109: 
  110: %% missing_struct_ tests verify the behaviour of packet_to_component handler,
  111: %% which is supposed to update the mapping of the sender in Redis and cache.
  112: %% In case of routing between nodes in single cluster AND routers being reordered
  113: %% with component routers at the beginning of the chain, this hook must not fail
  114: %% despite lack of global_distrib structure in Acc.
  115: missing_struct_in_message_from_user(_Config) ->
  116:     From = jid:make(<<"user">>, global_host(), <<"resource">>),
  117:     {Acc, To} = fake_acc_to_component(From),
  118:     % The handler must not crash and return unchanged Acc
  119:     Acc = mod_global_distrib_mapping:packet_to_component(Acc, From, To).
  120: 
  121: %% Update logic has two separate paths: when a packet is sent by a user or by another
  122: %% component. This test covers the latter.
  123: missing_struct_in_message_from_component(_Config) ->
  124:     From = jid:make(<<"">>, <<"from_service.", (global_host())/binary>>, <<"">>),
  125:     {Acc, To} = fake_acc_to_component(From),
  126:     % The handler must not crash and return unchanged Acc
  127:     Acc = mod_global_distrib_mapping:packet_to_component(Acc, From, To).
  128: 
  129: %%--------------------------------------------------------------------
  130: %% Helpers
  131: %%--------------------------------------------------------------------
  132: 
  133: global_host() ->
  134:     <<"localhost">>.
  135: 
  136: local_host() ->
  137:     <<"localhost.bis">>.
  138: 
  139: -spec fake_acc_to_component(From :: jid:jid()) -> {Acc :: mongoose_acc:t(), To :: jid:jid()}.
  140: fake_acc_to_component(From) ->
  141:     To = jid:make(<<"">>, <<"to_service.localhost">>, <<"">>),
  142:     FromBin = jid:to_binary(From),
  143:     ToBin = jid:to_binary(To),
  144:     BodyEl = #xmlel{
  145:                 name = <<"body">>,
  146:                 children = [#xmlcdata{ content = <<"hooks test">> }]
  147:                },
  148:     Packet = #xmlel{
  149:                 name = <<"message">>,
  150:                 attrs = [{<<"from">>, FromBin}, {<<"to">>, ToBin}, {<<"type">>, <<"chat">>}],
  151:                 children = [BodyEl]
  152:                },
  153:     {mongoose_acc:new(#{ location => ?LOCATION,
  154:                          lserver => From#jid.lserver,
  155:                          host_type => From#jid.lserver,
  156:                          element => Packet }), To}.
  157: 
  158: %%--------------------------------------------------------------------
  159: %% Meck
  160: %%--------------------------------------------------------------------
  161: 
  162: set_meck() ->
  163:     meck:new(mongoose_metrics, [stub_all]),
  164:     meck:new(mod_global_distrib_mapping_backend, [stub_all]),
  165:     %% Simulate missing entries and inserts into Redis
  166:     meck:expect(mod_global_distrib_mapping_backend, get_session, fun(_) -> error end),
  167:     meck:expect(mod_global_distrib_mapping_backend, get_domain, fun(_) -> error end).
  168: 
  169: unset_meck() ->
  170:     meck:unload(mod_global_distrib_mapping_backend),
  171:     meck:unload(mongoose_metrics).