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_opts(opts()),
   69:     mongoose_domain_sup:start_link(),
   70:     mim_ct_sup:start_link(ejabberd_sup),
   71:     mongooseim_helper:start_link_loaded_hooks(),
   72:     mongoose_modules:start(),
   73:     Config.
   74: 
   75: end_per_testcase(_CaseName, Config) ->
   76:     mongoose_modules:stop(),
   77:     mongoose_config:erase_opts(),
   78:     unset_meck(),
   79:     Config.
   80: 
   81: opts() ->
   82:     maps:from_list([{hosts, hosts()},
   83:                     {host_types, []},
   84:                     {all_metrics_are_global, false} |
   85:                     [{{modules, HostType}, modules(HostType)} || HostType <- hosts()]]).
   86: 
   87: hosts() ->
   88:     [global_host(), local_host()].
   89: 
   90: modules(HostType) ->
   91:     gen_mod_deps:resolve_deps(HostType, #{mod_global_distrib => module_opts()}).
   92: 
   93: module_opts() ->
   94:     mod_config(mod_global_distrib, #{global_host => global_host(),
   95:                                      local_host => local_host(),
   96:                                      connections => connection_opts()}).
   97: 
   98: connection_opts() ->
   99:     config([modules, mod_global_distrib, connections],
  100:            #{endpoints => [],
  101:              resolved_endpoints => [],
  102:              advertised_endpoints => []}).
  103: 
  104: %%--------------------------------------------------------------------
  105: %% Hook handlers tests
  106: %%--------------------------------------------------------------------
  107: 
  108: 
  109: %% missing_struct_ tests verify the behaviour of packet_to_component handler,
  110: %% which is supposed to update the mapping of the sender in Redis and cache.
  111: %% In case of routing between nodes in single cluster AND routers being reordered
  112: %% with component routers at the beginning of the chain, this hook must not fail
  113: %% despite lack of global_distrib structure in Acc.
  114: missing_struct_in_message_from_user(_Config) ->
  115:     From = jid:make(<<"user">>, global_host(), <<"resource">>),
  116:     {Acc, _To} = fake_acc_to_component(From),
  117:     % The handler must not crash and return unchanged Acc
  118:     {ok, Acc} = mod_global_distrib_mapping:packet_to_component(Acc, #{from => From}, #{}).
  119: 
  120: %% Update logic has two separate paths: when a packet is sent by a user or by another
  121: %% component. This test covers the latter.
  122: missing_struct_in_message_from_component(_Config) ->
  123:     From = jid:make(<<"">>, <<"from_service.", (global_host())/binary>>, <<"">>),
  124:     {Acc, _To} = fake_acc_to_component(From),
  125:     % The handler must not crash and return unchanged Acc
  126:     {ok, Acc} = mod_global_distrib_mapping:packet_to_component(Acc, #{from => From}, #{}).
  127: 
  128: %%--------------------------------------------------------------------
  129: %% Helpers
  130: %%--------------------------------------------------------------------
  131: 
  132: global_host() ->
  133:     <<"localhost">>.
  134: 
  135: local_host() ->
  136:     <<"localhost.bis">>.
  137: 
  138: -spec fake_acc_to_component(From :: jid:jid()) -> {Acc :: mongoose_acc:t(), To :: jid:jid()}.
  139: fake_acc_to_component(From) ->
  140:     To = jid:make(<<"">>, <<"to_service.localhost">>, <<"">>),
  141:     FromBin = jid:to_binary(From),
  142:     ToBin = jid:to_binary(To),
  143:     BodyEl = #xmlel{
  144:                 name = <<"body">>,
  145:                 children = [#xmlcdata{ content = <<"hooks test">> }]
  146:                },
  147:     Packet = #xmlel{
  148:                 name = <<"message">>,
  149:                 attrs = [{<<"from">>, FromBin}, {<<"to">>, ToBin}, {<<"type">>, <<"chat">>}],
  150:                 children = [BodyEl]
  151:                },
  152:     {mongoose_acc:new(#{ location => ?LOCATION,
  153:                          lserver => From#jid.lserver,
  154:                          host_type => From#jid.lserver,
  155:                          element => Packet }), To}.
  156: 
  157: %%--------------------------------------------------------------------
  158: %% Meck
  159: %%--------------------------------------------------------------------
  160: 
  161: set_meck() ->
  162:     meck:new(mongoose_metrics, [stub_all]),
  163:     meck:new(mod_global_distrib_mapping_backend, [stub_all]),
  164:     %% Simulate missing entries and inserts into Redis
  165:     meck:expect(mod_global_distrib_mapping_backend, get_session, fun(_) -> error end),
  166:     meck:expect(mod_global_distrib_mapping_backend, get_domain, fun(_) -> error end).
  167: 
  168: unset_meck() ->
  169:     meck:unload(mod_global_distrib_mapping_backend),
  170:     meck:unload(mongoose_metrics).