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