1: -module(mongoose_lazy_routing_SUITE).
    2: 
    3: -compile([export_all, nowarn_export_all]).
    4: 
    5: -include_lib("eunit/include/eunit.hrl").
    6: 
    7: -define(assertEqualLists(L1, L2), ?assertEqual(lists:sort(L1), lists:sort(L2))).
    8: 
    9: -define(HOST_TYPE_1, <<"host type #1">>).
   10: -define(HOST_TYPE_2, <<"host type #2">>).
   11: 
   12: -define(DOMAIN_1, <<"domain1.test">>).
   13: -define(DOMAIN_2, <<"domain2.test">>).
   14: -define(DOMAIN_3, <<"domain3.test">>).
   15: -define(SUBDOMAIN_1, <<"sub.", (?DOMAIN_1)/binary>>).
   16: -define(SUBDOMAIN_2, <<"sub.", (?DOMAIN_2)/binary>>).
   17: -define(SUBDOMAIN_3, <<"sub.", (?DOMAIN_3)/binary>>).
   18: 
   19: %% this domain is required for testing domain/subdomain conflict resolution.
   20: -define(DOMAIN_X, <<"domain_x.test">>).
   21: 
   22: %% required for handles_missing_domain_or_subdomain test case
   23: -define(MISSING_DOMAIN, <<"missing.domain.test">>).
   24: -define(MISSING_DOMAIN_2, <<"missing.domain2.test">>).
   25: 
   26: -define(NAMESPACE_1, <<"dummy:namespace:1">>).
   27: -define(NAMESPACE_2, <<"dummy:namespace:2">>).
   28: -define(COMPONENT, dummy_component).
   29: 
   30: -import(mongoose_lazy_routing, [maybe_add_domain_or_subdomain/1,
   31:                                 maybe_remove_domain/2,
   32:                                 maybe_remove_subdomain/1,
   33:                                 register_iq_handler_for_domain/4,
   34:                                 register_iq_handler_for_subdomain/5,
   35:                                 unregister_iq_handler_for_domain/3,
   36:                                 unregister_iq_handler_for_subdomain/4]).
   37: 
   38: all() ->
   39:     [can_add_and_remove_domain_or_subdomain,
   40:      handles_missing_domain_or_subdomain,
   41:      registers_top_level_domain_in_case_domain_subdomain_conflicts,
   42:      can_register_and_unregister_iq_handler_for_two_domains,
   43:      can_add_domain_for_a_registered_iq_handler,
   44:      can_register_and_unregister_iq_handler_for_two_subdomains,
   45:      can_add_subdomain_for_a_registered_iq_handler,
   46:      handles_double_iq_handler_registration_deregistration_for_domain,
   47:      handles_double_iq_handler_registration_deregistration_for_subdomain].
   48: 
   49: init_per_testcase(_, Config) ->
   50:     mongoose_lazy_routing:start(),
   51:     setup_meck(),
   52:     Config.
   53: 
   54: end_per_testcase(_, Config) ->
   55:     mongoose_lazy_routing:stop(),
   56:     meck:unload(),
   57:     Config.
   58: 
   59: %%-------------------------------------------------------------------
   60: %% test cases
   61: %%-------------------------------------------------------------------
   62: can_add_and_remove_domain_or_subdomain(_Config) ->
   63:     %% add 2 domains, one of them add twice
   64:     ?assertEqual(true, maybe_add_domain_or_subdomain(?DOMAIN_1)),
   65:     ?assertEqual(true, maybe_add_domain_or_subdomain(?DOMAIN_2)),
   66:     ?assertEqual(true, maybe_add_domain_or_subdomain(?DOMAIN_2)),
   67:     %% add 2 subdomains, one of them add twice
   68:     ?assertEqual(true, maybe_add_domain_or_subdomain(?SUBDOMAIN_1)),
   69:     ?assertEqual(true, maybe_add_domain_or_subdomain(?SUBDOMAIN_2)),
   70:     ?assertEqual(true, maybe_add_domain_or_subdomain(?SUBDOMAIN_2)),
   71:     %% check that 2 domains and 2 subdomains added properly
   72:     ?assertEqualLists([?DOMAIN_1, ?DOMAIN_2], get_all_registered_domains()),
   73:     ?assertEqualLists([{?SUBDOMAIN_1, packet_handler(?SUBDOMAIN_1)},
   74:                        {?SUBDOMAIN_2, packet_handler(?SUBDOMAIN_2)}],
   75:                       get_all_registered_subdomains()),
   76:     ?assertEqual([], get_all_unregistered_domains()),
   77:     ?assertEqual([], get_all_unregistered_subdomains()),
   78:     [meck:reset(M) || M <- [ejabberd_local, ejabberd_router]],
   79:     %% remove 2 domains, one of them remove twice
   80:     maybe_remove_domain(domain_host_type(?DOMAIN_1), ?DOMAIN_1),
   81:     maybe_remove_domain(domain_host_type(?DOMAIN_2), ?DOMAIN_2),
   82:     maybe_remove_domain(domain_host_type(?DOMAIN_2), ?DOMAIN_2),
   83:     %% remove 2 subdomains, one of them remove twice
   84:     maybe_remove_subdomain(subdomain_info(?SUBDOMAIN_1)),
   85:     maybe_remove_subdomain(subdomain_info(?SUBDOMAIN_2)),
   86:     maybe_remove_subdomain(subdomain_info(?SUBDOMAIN_2)),
   87:     %% check that 2 domains and 2 subdomains removed properly
   88:     mongoose_lazy_routing:sync(),
   89:     ?assertEqualLists([], get_all_registered_domains()),
   90:     ?assertEqualLists([], get_all_registered_subdomains()),
   91:     ?assertEqual([?SUBDOMAIN_1, ?SUBDOMAIN_2], get_all_unregistered_subdomains()),
   92:     ?assertEqual([?DOMAIN_1, ?DOMAIN_2], get_all_unregistered_domains()).
   93: 
   94: handles_missing_domain_or_subdomain(_Config) ->
   95:     %% ?MISSING_DOMAIN is used to emulate domain/subdomain removal
   96:     %% before mongoose_lazy_routing processes domain registration
   97:     ?assertEqual(false, maybe_add_domain_or_subdomain(?MISSING_DOMAIN)),
   98:     ?assertEqual(false, maybe_add_domain_or_subdomain(?MISSING_DOMAIN_2)),
   99:     ?assertEqual(1, meck:num_calls(mongoose_lazy_routing, handle_call,
  100:                                    [{maybe_add_domain_or_subdomain, ?MISSING_DOMAIN},
  101:                                     '_', '_'])),
  102:     ?assertEqual(0, meck:num_calls(mongoose_lazy_routing, handle_call,
  103:                                    [{maybe_add_domain_or_subdomain, ?MISSING_DOMAIN_2},
  104:                                     '_', '_'])).
  105: 
  106: registers_top_level_domain_in_case_domain_subdomain_conflicts(_Config) ->
  107:     %% add 2 times domain which has name collision with subdomain
  108:     ?assertEqual(true, maybe_add_domain_or_subdomain(?DOMAIN_X)),
  109:     ?assertEqual(true, maybe_add_domain_or_subdomain(?DOMAIN_X)),
  110:     %% check that only top level domain is added
  111:     ?assertEqualLists([?DOMAIN_X], get_all_registered_domains()),
  112:     ?assertEqualLists([], get_all_registered_subdomains()),
  113:     ?assertEqual([], get_all_unregistered_domains()),
  114:     ?assertEqual([], get_all_unregistered_subdomains()),
  115:     [meck:reset(M) || M <- [ejabberd_local, ejabberd_router]],
  116:     %% try to remove that domain and subdomain
  117:     maybe_remove_domain(domain_host_type(?DOMAIN_X), ?DOMAIN_X),
  118:     maybe_remove_subdomain(subdomain_info(?DOMAIN_X)),
  119:     mongoose_lazy_routing:sync(),
  120:     %% check that only top level domain is removed
  121:     ?assertEqualLists([], get_all_registered_domains()),
  122:     ?assertEqualLists([], get_all_registered_subdomains()),
  123:     ?assertEqual([], get_all_unregistered_subdomains()),
  124:     ?assertEqual([?DOMAIN_X], get_all_unregistered_domains()).
  125: 
  126: can_register_and_unregister_iq_handler_for_two_domains(_Config) ->
  127:     %% add 2 domains for ?HOST_TYPE_2 and one subdomain (just to ensure that subdomain
  128:     %% doesn't affect anything)
  129:     ?assertEqual(true, maybe_add_domain_or_subdomain(?DOMAIN_2)),
  130:     ?assertEqual(true, maybe_add_domain_or_subdomain(?DOMAIN_3)),
  131:     ?assertEqual(true, maybe_add_domain_or_subdomain(?SUBDOMAIN_2)),
  132:     [begin %% repeat twice
  133:          %% register IQ handler for ?HOST_TYPE_2 domains
  134:          IQHandlerWithHostType = create_iq_handler_and_register(<<"IQH">>, ?HOST_TYPE_2,
  135:                                                                 ?NAMESPACE_1, ?COMPONENT),
  136:          ?assertEqualLists([{?COMPONENT, ?DOMAIN_2, ?NAMESPACE_1, IQHandlerWithHostType},
  137:                             {?COMPONENT, ?DOMAIN_3, ?NAMESPACE_1, IQHandlerWithHostType}],
  138:                            get_all_registered_iqs()),
  139:          ?assertEqual([], get_all_unregistered_iqs()),
  140:          meck:reset(gen_iq_component),
  141:          %% unregister IQ handler for ?HOST_TYPE_2 domains
  142:          ?assertEqual({ok, IQHandlerWithHostType},
  143:                       unregister_iq_handler_for_domain(?HOST_TYPE_2, ?NAMESPACE_1,
  144:                                                        ?COMPONENT)),
  145:          ?assertEqual([], get_all_registered_iqs()),
  146:          ?assertEqualLists([{?COMPONENT, ?DOMAIN_2, ?NAMESPACE_1},
  147:                             {?COMPONENT, ?DOMAIN_3, ?NAMESPACE_1}],
  148:                            get_all_unregistered_iqs()),
  149:          meck:reset(gen_iq_component)
  150:      end || _ <- lists:seq(1, 2)],
  151:     %% remove 2 domains and one subdomain for ?HOST_TYPE_2
  152:     maybe_remove_domain(domain_host_type(?DOMAIN_2), ?DOMAIN_2),
  153:     maybe_remove_domain(domain_host_type(?DOMAIN_3), ?DOMAIN_3),
  154:     maybe_remove_subdomain(subdomain_info(?SUBDOMAIN_2)),
  155:     ?assertEqual([], get_all_registered_iqs()),
  156:     ?assertEqual([], get_all_unregistered_iqs()).
  157: 
  158: can_add_domain_for_a_registered_iq_handler(_Config) ->
  159:     %%-------------------------------------------------------------------------------
  160:     %% this test case consists of the following steps:
  161:     %% 1) register 2 IQ handlers for ?HOST_TYPE_1 domains
  162:     %% 2) add ?SUBDOMAIN_1 (?HOST_TYPE_1, just to ensure that subdomain adding
  163:     %%    doesn't affect anything)
  164:     %% 3) add and then remove ?DOMAIN_1, check that it leads to the execution of
  165:     %%    the proper gen_iq_component interfaces (run this step twice)
  166:     %% 4) unregister 2 IQ handlers ?HOST_TYPE_1 domains
  167:     %%-------------------------------------------------------------------------------
  168: 
  169:     %% register 2 IQ handlers
  170:     IQHandlerWithHostType1 = create_iq_handler_and_register(<<"IQH1">>, ?HOST_TYPE_1,
  171:                                                             ?NAMESPACE_1, ?COMPONENT),
  172:     IQHandlerWithHostType2 = create_iq_handler_and_register(<<"IQH2">>, ?HOST_TYPE_1,
  173:                                                             ?NAMESPACE_2, ?COMPONENT),
  174:     %% add ?SUBDOMAIN_1
  175:     ?assertEqual(true, maybe_add_domain_or_subdomain(?SUBDOMAIN_1)),
  176:     ?assertEqual([], get_all_registered_iqs()),
  177:     [begin %% repeat twice
  178:          %% add ?DOMAIN_1
  179:          ?assertEqual(true, maybe_add_domain_or_subdomain(?DOMAIN_1)),
  180:          ?assertEqualLists([{?COMPONENT, ?DOMAIN_1, ?NAMESPACE_1,
  181:                              IQHandlerWithHostType1},
  182:                             {?COMPONENT, ?DOMAIN_1, ?NAMESPACE_2,
  183:                              IQHandlerWithHostType2}],
  184:                            get_all_registered_iqs()),
  185:          ?assertEqual([], get_all_unregistered_iqs()),
  186:          meck:reset(gen_iq_component),
  187:          %% remove ?DOMAIN_1
  188:          maybe_remove_domain(?HOST_TYPE_1, ?DOMAIN_1),
  189:          mongoose_lazy_routing:sync(),
  190:          ?assertEqual([], get_all_registered_iqs()),
  191:          ?assertEqualLists([{?COMPONENT, ?DOMAIN_1, ?NAMESPACE_1},
  192:                             {?COMPONENT, ?DOMAIN_1, ?NAMESPACE_2}],
  193:                            get_all_unregistered_iqs()),
  194:          meck:reset(gen_iq_component)
  195:      end || _ <- lists:seq(1, 2)],
  196:     %% unregister 2 IQ handlers
  197:     ?assertEqual({ok, IQHandlerWithHostType1},
  198:                  unregister_iq_handler_for_domain(?HOST_TYPE_1, ?NAMESPACE_1,
  199:                                                   ?COMPONENT)),
  200:     ?assertEqual({ok, IQHandlerWithHostType2},
  201:                  unregister_iq_handler_for_domain(?HOST_TYPE_1, ?NAMESPACE_2,
  202:                                                   ?COMPONENT)),
  203:     ?assertEqual([], get_all_unregistered_iqs()),
  204:     ?assertEqual([], get_all_registered_iqs()).
  205: 
  206: can_register_and_unregister_iq_handler_for_two_subdomains(_Config) ->
  207:     %% add 2 subdomains for ?HOST_TYPE_2 and one domain (just to ensure that domain
  208:     %% doesn't affect anything)
  209:     ?assertEqual(true, maybe_add_domain_or_subdomain(?SUBDOMAIN_2)),
  210:     ?assertEqual(true, maybe_add_domain_or_subdomain(?SUBDOMAIN_3)),
  211:     ?assertEqual(true, maybe_add_domain_or_subdomain(?DOMAIN_2)),
  212:     Pattern = subdomain_pattern(?SUBDOMAIN_2),
  213:     [begin %% repeat twice
  214:          %% register IQ handler for ?HOST_TYPE_2 subdomains
  215:          IQHandlerWithHostType = create_iq_handler_and_register(<<"IQH">>, ?HOST_TYPE_2,
  216:                                                                 Pattern, ?NAMESPACE_1,
  217:                                                                 ?COMPONENT),
  218:          ?assertEqualLists([{?COMPONENT, ?SUBDOMAIN_2, ?NAMESPACE_1,
  219:                              IQHandlerWithHostType},
  220:                             {?COMPONENT, ?SUBDOMAIN_3, ?NAMESPACE_1,
  221:                              IQHandlerWithHostType}],
  222:                            get_all_registered_iqs()),
  223:          ?assertEqual([], get_all_unregistered_iqs()),
  224:          meck:reset(gen_iq_component),
  225:          %% unregister IQ handler for ?HOST_TYPE_2 subdomains
  226:          ?assertEqual({ok, IQHandlerWithHostType},
  227:                       unregister_iq_handler_for_subdomain(?HOST_TYPE_2, Pattern,
  228:                                                           ?NAMESPACE_1, ?COMPONENT)),
  229:          ?assertEqual([], get_all_registered_iqs()),
  230:          ?assertEqualLists([{?COMPONENT, ?SUBDOMAIN_2, ?NAMESPACE_1},
  231:                             {?COMPONENT, ?SUBDOMAIN_3, ?NAMESPACE_1}],
  232:                            get_all_unregistered_iqs()),
  233:          meck:reset(gen_iq_component)
  234:      end || _ <- lists:seq(1, 2)],
  235:     %% remove 2 subdomains and one domain for ?HOST_TYPE_2
  236:     maybe_remove_subdomain(subdomain_info(?SUBDOMAIN_2)),
  237:     maybe_remove_subdomain(subdomain_info(?SUBDOMAIN_3)),
  238:     maybe_remove_domain(domain_host_type(?DOMAIN_2), ?DOMAIN_2),
  239:     ?assertEqual([], get_all_registered_iqs()),
  240:     ?assertEqual([], get_all_unregistered_iqs()).
  241: 
  242: can_add_subdomain_for_a_registered_iq_handler(_Config) ->
  243:     %%-------------------------------------------------------------------------------
  244:     %% this test case consists of the following steps:
  245:     %% 1) register 2 IQ handlers for ?HOST_TYPE_1 subdomains
  246:     %% 2) add ?DOMAIN_1 (?HOST_TYPE_1, just to ensure that domain adding
  247:     %%    doesn't affect anything)
  248:     %% 3) add and then remove ?SUBDOMAIN_1, check that it leads to the execution of
  249:     %%    the proper gen_iq_component interfaces (run this step twice)
  250:     %% 4) unregister 2 IQ handlers ?HOST_TYPE_1 subdomains
  251:     %%-------------------------------------------------------------------------------
  252: 
  253:     %% register 2 IQ handlers
  254:     Pattern = subdomain_pattern(?SUBDOMAIN_1),
  255:     IQHandlerWithHostType1 = create_iq_handler_and_register(<<"IQH1">>, ?HOST_TYPE_1,
  256:                                                             Pattern, ?NAMESPACE_1,
  257:                                                             ?COMPONENT),
  258:     IQHandlerWithHostType2 = create_iq_handler_and_register(<<"IQH2">>, ?HOST_TYPE_1,
  259:                                                             Pattern, ?NAMESPACE_2,
  260:                                                             ?COMPONENT),
  261:     %% add ?SUBDOMAIN_1
  262:     ?assertEqual(true, maybe_add_domain_or_subdomain(?DOMAIN_1)),
  263:     ?assertEqual([], get_all_registered_iqs()),
  264:     [begin %% repeat twice
  265:          %% add ?SUBDOMAIN_1
  266:          ?assertEqual(true, maybe_add_domain_or_subdomain(?SUBDOMAIN_1)),
  267:          ?assertEqualLists([{?COMPONENT, ?SUBDOMAIN_1, ?NAMESPACE_1,
  268:                              IQHandlerWithHostType1},
  269:                             {?COMPONENT, ?SUBDOMAIN_1, ?NAMESPACE_2,
  270:                              IQHandlerWithHostType2}],
  271:                            get_all_registered_iqs()),
  272:          ?assertEqual([], get_all_unregistered_iqs()),
  273:          meck:reset(gen_iq_component),
  274:          %% remove ?SUBDOMAIN_1
  275:          maybe_remove_subdomain(subdomain_info(?SUBDOMAIN_1)),
  276:          mongoose_lazy_routing:sync(),
  277:          ?assertEqual([], get_all_registered_iqs()),
  278:          ?assertEqualLists([{?COMPONENT, ?SUBDOMAIN_1, ?NAMESPACE_1},
  279:                             {?COMPONENT, ?SUBDOMAIN_1, ?NAMESPACE_2}],
  280:                            get_all_unregistered_iqs()),
  281:          meck:reset(gen_iq_component)
  282:      end || _ <- lists:seq(1, 2)],
  283:     %% unregister 2 IQ handlers
  284:     ?assertEqual({ok, IQHandlerWithHostType1},
  285:                  unregister_iq_handler_for_subdomain(?HOST_TYPE_1, Pattern,
  286:                                                      ?NAMESPACE_1, ?COMPONENT)),
  287:     ?assertEqual({ok, IQHandlerWithHostType2},
  288:                  unregister_iq_handler_for_subdomain(?HOST_TYPE_1, Pattern,
  289:                                                      ?NAMESPACE_2, ?COMPONENT)),
  290:     ?assertEqual([], get_all_unregistered_iqs()),
  291:     ?assertEqual([], get_all_registered_iqs()).
  292: 
  293: handles_double_iq_handler_registration_deregistration_for_domain(_Config) ->
  294:     %% add one domain and register IQ handler for it.
  295:     ?assertEqual(true, maybe_add_domain_or_subdomain(?DOMAIN_1)),
  296:     IQHandlerWithHostType = create_iq_handler_and_register(<<"IQH">>, ?HOST_TYPE_1,
  297:                                                            ?NAMESPACE_1, ?COMPONENT),
  298:     ?assertEqual([{?COMPONENT, ?DOMAIN_1, ?NAMESPACE_1, IQHandlerWithHostType}],
  299:                  get_all_registered_iqs()),
  300:     meck:reset(gen_iq_component),
  301:     %% try to register IQ handler for the same host type,
  302:     %% component and namespace one more time.
  303:     ?assertEqual({error, already_registered},
  304:                  register_iq_handler_for_domain(?HOST_TYPE_1, ?NAMESPACE_1, ?COMPONENT,
  305:                                                 IQHandlerWithHostType)),
  306:     ?assertEqual([], get_all_registered_iqs()),
  307:     ?assertEqual([], get_all_unregistered_iqs()),
  308:     %% unregister IQ handler.
  309:     ?assertEqual({ok, IQHandlerWithHostType},
  310:                  unregister_iq_handler_for_domain(?HOST_TYPE_1, ?NAMESPACE_1,
  311:                                                   ?COMPONENT)),
  312:     ?assertEqual([{?COMPONENT, ?DOMAIN_1, ?NAMESPACE_1}], get_all_unregistered_iqs()),
  313:     meck:reset(gen_iq_component),
  314:     %% try unregister IQ handler one more time.
  315:     ?assertEqual({error, not_found},
  316:                  unregister_iq_handler_for_domain(?HOST_TYPE_1, ?NAMESPACE_1,
  317:                                                   ?COMPONENT)),
  318:     ?assertEqual([], get_all_registered_iqs()),
  319:     ?assertEqual([], get_all_unregistered_iqs()).
  320: 
  321: handles_double_iq_handler_registration_deregistration_for_subdomain(_Config) ->
  322:     %% add one subdomain and register IQ handler for it.
  323:     Pattern = subdomain_pattern(?SUBDOMAIN_1),
  324:     ?assertEqual(true, maybe_add_domain_or_subdomain(?SUBDOMAIN_1)),
  325:     IQHandlerWithHostType = create_iq_handler_and_register(<<"IQH">>, ?HOST_TYPE_1,
  326:                                                            Pattern, ?NAMESPACE_1,
  327:                                                            ?COMPONENT),
  328:     ?assertEqual([{?COMPONENT, ?SUBDOMAIN_1, ?NAMESPACE_1, IQHandlerWithHostType}],
  329:                  get_all_registered_iqs()),
  330:     meck:reset(gen_iq_component),
  331:     %% try to register IQ handler for the same subdomain pattern,
  332:     %% component and namespace one more time.
  333:     ?assertEqual({error, already_registered},
  334:                  register_iq_handler_for_subdomain(?HOST_TYPE_1, Pattern, ?NAMESPACE_1,
  335:                                                    ?COMPONENT, IQHandlerWithHostType)),
  336:     ?assertEqual([], get_all_registered_iqs()),
  337:     ?assertEqual([], get_all_unregistered_iqs()),
  338:     %% unregister IQ handler.
  339:     ?assertEqual({ok, IQHandlerWithHostType},
  340:                  unregister_iq_handler_for_subdomain(?HOST_TYPE_1, Pattern,
  341:                                                      ?NAMESPACE_1, ?COMPONENT)),
  342:     ?assertEqual([{?COMPONENT, ?SUBDOMAIN_1, ?NAMESPACE_1}],
  343:                  get_all_unregistered_iqs()),
  344:     meck:reset(gen_iq_component),
  345:     %% try unregister IQ handler one more time.
  346:     ?assertEqual({error, not_found},
  347:                  unregister_iq_handler_for_subdomain(?HOST_TYPE_1, Pattern,
  348:                                                      ?NAMESPACE_1, ?COMPONENT)),
  349:     ?assertEqual([], get_all_registered_iqs()),
  350:     ?assertEqual([], get_all_unregistered_iqs()).
  351: 
  352: %%-------------------------------------------------------------------
  353: %% internal functions
  354: %%-------------------------------------------------------------------
  355: setup_meck() ->
  356:     Modules = [ejabberd_local, ejabberd_router, gen_iq_component,
  357:                mongoose_domain_core, mongoose_subdomain_core],
  358:     [meck:new(M, [no_link]) || M <- Modules],
  359:     meck:new(mongoose_lazy_routing, [no_link, passthrough]),
  360:     meck:expect(ejabberd_local, register_host, fun(_) -> ok end),
  361:     meck:expect(ejabberd_local, unregister_host, fun(_) -> ok end),
  362:     meck:expect(ejabberd_router, unregister_route, fun(_) -> ok end),
  363:     meck:expect(ejabberd_router, register_route, fun(_, _) -> ok end),
  364:     meck:expect(gen_iq_component, register_iq_handler, fun(_, _, _, _) -> ok end),
  365:     meck:expect(gen_iq_component, sync, fun(_) -> ok end),
  366:     meck:expect(gen_iq_component, unregister_iq_handler, fun(_, _, _) -> ok end),
  367:     meck:expect(mongoose_domain_core, get_host_type, fun get_domain_host_type/1),
  368:     meck:expect(mongoose_subdomain_core, get_host_type, fun get_subdomain_host_type/1),
  369:     meck:expect(mongoose_subdomain_core, get_subdomain_info, fun get_subdomain_info/1).
  370: 
  371: predefined_domains() ->
  372:     #{?DOMAIN_1 => ?HOST_TYPE_1,
  373:       ?DOMAIN_2 => ?HOST_TYPE_2,
  374:       ?DOMAIN_3 => ?HOST_TYPE_2,
  375:       ?DOMAIN_X => ?HOST_TYPE_1}.
  376: 
  377: -spec predefined_subdomains() ->
  378:     #{jid:lserver() => mongoose_subdomain_core:subdomain_info()}.
  379: predefined_subdomains() ->
  380:     SubdomainPattern = mongoose_subdomain_utils:make_subdomain_pattern(<<"sub.@HOST@">>),
  381:     FQDNPattern = mongoose_subdomain_utils:make_subdomain_pattern(?DOMAIN_X),
  382:     #{?SUBDOMAIN_1 =>
  383:           #{host_type => ?HOST_TYPE_1, subdomain => ?SUBDOMAIN_1,
  384:             subdomain_pattern => SubdomainPattern, parent_domain => ?DOMAIN_1,
  385:             packet_handler => mongoose_packet_handler:new(packet_handler_1,
  386:                                                           #{host_type => ?HOST_TYPE_1})},
  387:       ?SUBDOMAIN_2 =>
  388:           #{host_type => ?HOST_TYPE_2, subdomain => ?SUBDOMAIN_2,
  389:             subdomain_pattern => SubdomainPattern, parent_domain => ?DOMAIN_2,
  390:             packet_handler => mongoose_packet_handler:new(packet_handler_2,
  391:                                                           #{host_type => ?HOST_TYPE_2})},
  392:       ?SUBDOMAIN_3 =>
  393:           #{host_type => ?HOST_TYPE_2, subdomain => ?SUBDOMAIN_3,
  394:             subdomain_pattern => SubdomainPattern, parent_domain => ?DOMAIN_3,
  395:             packet_handler => mongoose_packet_handler:new(packet_handler_3,
  396:                                                           #{host_type => ?HOST_TYPE_2})},
  397:       ?DOMAIN_X =>
  398:           #{host_type => ?HOST_TYPE_1, subdomain => ?DOMAIN_X,
  399:             subdomain_pattern => FQDNPattern, parent_domain => no_parent_domain,
  400:             packet_handler => mongoose_packet_handler:new(packet_handler_x,
  401:                                                           #{host_type => ?HOST_TYPE_1})}}.
  402: 
  403: get_domain_host_type(Domain) ->
  404:     case maps:get(Domain, predefined_domains(), undefined) of
  405:         undefined -> {error, not_found};
  406:         HostType -> {ok, HostType}
  407:     end.
  408: 
  409: domain_host_type(Domain) ->
  410:     element(2, get_domain_host_type(Domain)).
  411: 
  412: get_subdomain_host_type(?MISSING_DOMAIN) ->
  413:     %% required for handles_domain_or_subdomain_removal_while_adding test case
  414:     {ok, ?HOST_TYPE_1};
  415: get_subdomain_host_type(Subdomain) ->
  416:     case maps:get(Subdomain, predefined_subdomains(), undefined) of
  417:         undefined -> {error, not_found};
  418:         SubdomainInfo -> {ok, maps:get(host_type, SubdomainInfo)}
  419:     end.
  420: 
  421: get_subdomain_info(Subdomain) ->
  422:     case maps:get(Subdomain, predefined_subdomains(), undefined) of
  423:         undefined -> {error, not_found};
  424:         SubdomainInfo -> {ok, SubdomainInfo}
  425:     end.
  426: 
  427: subdomain_info(Subdomain) ->
  428:     element(2, get_subdomain_info(Subdomain)).
  429: 
  430: packet_handler(Subdomain) ->
  431:     maps:get(packet_handler, maps:get(Subdomain, predefined_subdomains())).
  432: 
  433: subdomain_pattern(Subdomain) ->
  434:     maps:get(subdomain_pattern, maps:get(Subdomain, predefined_subdomains())).
  435: 
  436: iq_handler(Extra) ->
  437:     FN = fun(Acc, _From, _To, _IQ, _Extra) -> {Acc, ignore} end,
  438:     %% using no_queue execution type to ensure this function returns
  439:     %% one the same IQ handler if we call it with the same parameters.
  440:     mongoose_iq_handler:new(FN, Extra, no_queue).
  441: 
  442: get_all_registered_domains() ->
  443:     History = meck:history(ejabberd_local),
  444:     [Domain || {_Pid, {_Mod, Func, [Domain] = _Args}, _Result} <- History,
  445:                Func =:= register_host].
  446: 
  447: get_all_unregistered_domains() ->
  448:     History = meck:history(ejabberd_local),
  449:     [Domain || {_Pid, {_Mod, Func, [Domain] = _Args}, _Result} <- History,
  450:                Func =:= unregister_host].
  451: 
  452: get_all_registered_subdomains() ->
  453:     History = meck:history(ejabberd_router),
  454:     [{Subdomain, PacketHandler}
  455:      || {_Pid, {_Mod, Func, [Subdomain, PacketHandler] = _Args}, _Result} <- History,
  456:         Func =:= register_route].
  457: 
  458: get_all_unregistered_subdomains() ->
  459:     History = meck:history(ejabberd_router),
  460:     [Subdomain || {_Pid, {_Mod, Func, [Subdomain] = _Args}, _Result} <- History,
  461:                   Func =:= unregister_route].
  462: 
  463: create_iq_handler_and_register(Tag, HostType, Namespace, Component) ->
  464:     IQHandler = iq_handler(#{tag => Tag}),
  465:     Ret = register_iq_handler_for_domain(HostType, Namespace, Component, IQHandler),
  466:     ?assertEqual(ok, Ret),
  467:     mongoose_iq_handler:add_extra(IQHandler, #{host_type => HostType}).
  468: 
  469: create_iq_handler_and_register(Tag, HostType, SubdomainPattern, Namespace, Component) ->
  470:     IQHandler = iq_handler(#{tag => Tag}),
  471:     Ret = register_iq_handler_for_subdomain(HostType, SubdomainPattern, Namespace,
  472:                                             Component, IQHandler),
  473:     ?assertEqual(ok, Ret),
  474:     mongoose_iq_handler:add_extra(IQHandler, #{host_type => HostType}).
  475: 
  476: get_all_registered_iqs() ->
  477:     History = meck:history(gen_iq_component),
  478:     [list_to_tuple(Args)
  479:      || {_Pid, {_Mod, register_iq_handler = _Func, Args}, _Result} <- History].
  480: 
  481: get_all_unregistered_iqs() ->
  482:     History = meck:history(gen_iq_component),
  483:     [list_to_tuple(Args)
  484:      || {_Pid, {_Mod, unregister_iq_handler = _Func, Args}, _Result} <- History].